Как преобразовать координаты глобальной позиции RenderBox для Tween <Offset>? - PullRequest
0 голосов
/ 21 января 2019

Я пытаюсь создать свой собственный переход в стиле Hero, используя SlideTransition с позицией Offset, начиная с того места, где пользователь касается элемента на предыдущем экране.

Это то, что яВ настоящее время я использую для получения значения координаты, где пользователь нажимает на экран (мне нужно только значение dy):

GestureDetector(
    child: //stuff
    onTapDown: (TapDownDetails details) async {
        RenderBox box = context.findRenderObject();
        double position = box.localToGlobal(details.globalPosition).dy;
            await Navigator.push(context, MaterialPageRoute(builder: (context) {
        return SecondPage(startPosition: position);
        }));
    },
)

Затем я передаю это на SecondPage и использую его какначальная позиция Animation<Offset> в моем initState:

@override
void initState() {
    controller = new AnimationController(
        duration: const Duration(milliseconds: 2000), vsync: this);
    curve = CurvedAnimation(parent: controller, curve: Curves.easeInOut);
    offset = new Tween<Offset>(begin: Offset(0.0, widget.startPosition), end: Offset.zero).animate(curve);
    controller.forward();
    super.initState();
}

Проблема, с которой я столкнулся, заключается в том, чтобы найти способ правильно преобразовать значение dy в значение, которое соответствует значению Tween<Offset>используется, поскольку значение dy входит со значением, скажем, 250-300 для половины пути вниз по экрану, но то же самое для Offset(0.0, widget.startPosition) будет около 2.0 для того, чтобы оно совпадало с той же позицией.Я пробовал различные математические расчеты, чтобы соответствовать этим (например, деление dy на высоту экранов), но я не нашел ничего, что точно соответствует этому.

Если кто-нибудь знает правильный метод / точные математические вычисления, яЯ должен любить тебя вечно.

Редактировать: Самостоятельный пример того, чего я пытаюсь достичь, с которым вы можете поиграть.В настоящее время я использую double position = (box.globalToLocal(details.globalPosition).dy) / box.size.height * 3;, так как это около ближайшего совпадения, которое я нашел.

import 'package:flutter/material.dart';

class HomePage extends StatefulWidget {
  @override
  State createState() => HomePageState();
}

class HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: Colors.indigo.shade200,
        floatingActionButton: Padding(
            padding: EdgeInsets.only(
                bottom: MediaQuery.of(context).size.height / 35),
            child: FloatingActionButton(
              child: Icon(Icons.add),
              backgroundColor: Colors.grey.shade200,
              foregroundColor: Colors.black,
              onPressed: () {},
            )),
        body: Stack(children: <Widget>[
          SingleChildScrollView(
              child: Padding(
                  padding: EdgeInsets.only(
                      bottom: MediaQuery.of(context).size.height / 11),
                  child:
                      Column(children: <Widget>[Stack(children: getCards())]))),
          Container(
              height: MediaQuery.of(context).size.height / 5,
              width: MediaQuery.of(context).size.width,
              decoration: BoxDecoration(
                  color: Colors.grey.shade200,
                  borderRadius:
                      BorderRadius.only(bottomLeft: Radius.circular(100.0))),
              child: Center(
                  child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                      children: <Widget>[
                    Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: <Widget>[
                        InkWell(
                            child: Padding(
                          padding: EdgeInsets.all(6.0),
                          child: Container(
                            height: 40.0,
                            width: 40.0,
                            decoration: BoxDecoration(
                                shape: BoxShape.circle,
                                border: Border.all(
                                    width: 2.0, color: Colors.pink.shade300)),
                            child: Icon(Icons.sentiment_satisfied),
                          ),
                        )),
                        Text('Tab1',
                            style: TextStyle(
                                fontSize: 10.0,
                                fontWeight: FontWeight.bold,
                                color: Colors.black38))
                      ],
                    ),
                    Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: <Widget>[
                        InkWell(
                            child: Padding(
                          padding: EdgeInsets.all(6.0),
                          child: Container(
                            height: 40.0,
                            width: 40.0,
                            decoration: BoxDecoration(
                                shape: BoxShape.circle,
                                border: Border.all(
                                    width: 2.0, color: Colors.pink.shade300)),
                            child: Icon(Icons.trending_up),
                          ),
                        )),
                        Text('Tab2',
                            style: TextStyle(
                                fontSize: 10.0,
                                fontWeight: FontWeight.bold,
                                color: Colors.black38))
                      ],
                    ),
                    Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: <Widget>[
                        InkWell(
                            child: Padding(
                          padding: EdgeInsets.all(6.0),
                          child: Container(
                            height: 40.0,
                            width: 40.0,
                            decoration: BoxDecoration(
                                shape: BoxShape.circle,
                                border: Border.all(
                                    width: 2.0, color: Colors.pink.shade300)),
                            child: Icon(Icons.favorite_border),
                          ),
                        )),
                        Text('Tab3',
                            style: TextStyle(
                                fontSize: 10.0,
                                fontWeight: FontWeight.bold,
                                color: Colors.black38))
                      ],
                    )
                  ])))
        ]));
  }

  List<Widget> getCards() {
    List<Widget> widgets = new List<Widget>();
    for (int i = 0; i < 5; i++) {
      widgets.add(GestureDetector(
        child: Container(
          margin: EdgeInsets.only(top: (i * 175).toDouble()),
          padding: EdgeInsets.all(60.0),
          height: 300.0,
          decoration: BoxDecoration(
            color: getColor(i),
            borderRadius: BorderRadius.only(bottomLeft: Radius.circular(100.0)),
          ),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.end,
            children: <Widget>[
              Center(
                      child: Text('Text ' + (i + 1).toString(),
                          maxLines: 2,
                          overflow: TextOverflow.ellipsis,
                          style: TextStyle(
                              color: Colors.white,
                              fontSize: 26.0,
                              fontWeight: FontWeight.bold)))
            ],
          ),
        ),
        onTapDown: (TapDownDetails details) async {
          RenderBox box = context.findRenderObject();
          double position = (box.globalToLocal(details.globalPosition).dy) / box.size.height * 3;
          await Navigator.push(context, CustomPageRoute(builder: (context) {
            return SecondPage(index: i, startPosition: position);
          }));
        },
      ));
    }
    return widgets.reversed.toList();
  }
}

class SecondPage extends StatefulWidget {
  final int index;
  final double startPosition;

  SecondPage({this.index, this.startPosition});

  @override
  State createState() => SecondPageState();
}

class SecondPageState extends State<SecondPage> with TickerProviderStateMixin {
  AnimationController controller;
  Animation curve;
  Animation<Offset> offset;

  @override
  void initState() {
    controller = new AnimationController(
        duration: const Duration(milliseconds: 2000), vsync: this);
    curve = CurvedAnimation(parent: controller, curve: Curves.easeInOut);
    offset = new Tween<Offset>(
            begin: Offset(0.0, widget.startPosition), end: Offset.zero)
        .animate(curve);
    controller.forward();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: Colors.indigo,
        body: Material(
            color: Colors.transparent,
            child: Stack(children: <Widget>[
              SlideTransition(
                  position: offset,
                  child: Container(
                      height: 200.0,
                      decoration: BoxDecoration(
                          color: getColor(widget.index),
                          borderRadius: BorderRadius.only(
                              bottomLeft: Radius.circular(100.0))),
                      child: Padding(
                          padding: EdgeInsets.only(top: 28.0),
                          child: Stack(
                            children: <Widget>[
                              Align(
                                  alignment: Alignment.topLeft,
                                  child: IconButton(
                                    icon: Icon(Icons.arrow_back,
                                        color: Colors.white),
                                    onPressed: () {
                                      Navigator.of(context).pop(true);
                                    },
                                  )),
                              Align(
                                  alignment: Alignment.topRight,
                                  child: IconButton(
                                    icon:
                                        Icon(Icons.launch, color: Colors.white),
                                    onPressed: () {
                                      print("launch website");
                                    },
                                  )),
                                  Align(
                                  alignment: Alignment.center,
                                  child: Padding(
                                      padding: EdgeInsets.symmetric(
                                          horizontal: MediaQuery.of(context)
                                                  .size
                                                  .width /
                                              5),
                                      child: Material(
                                            color: Colors.transparent,
                                            child: Text('Text ' + (widget.index + 1).toString(),
                                              maxLines: 2,
                                              overflow: TextOverflow.ellipsis,
                                              style: TextStyle(
                                                  color: Colors.white,
                                                  fontSize: 26.0,
                                                  fontWeight:
                                                      FontWeight.bold)))))
                            ],
                          ))))
            ])));
  }
}

class CustomPageRoute<T> extends MaterialPageRoute<T> {
  CustomPageRoute({WidgetBuilder builder, RouteSettings settings})
      : super(builder: builder, settings: settings);

  @override
  Widget buildTransitions(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation, Widget child) {
    return child;
  }
}

Color getColor(int index) {
  switch (index) {
    case 0:
      return Colors.pink.shade300;
    case 1:
      return Colors.purple.shade300;
    case 2:
      return Colors.deepPurple.shade400;
    case 3:
      return Colors.deepPurple.shade900;
    case 4:
      return Colors.indigo.shade900;
    default:
      return Colors.red;
  }
}

1 Ответ

0 голосов
/ 30 января 2019

Поиск renderBox в этом случае бесполезен, так как все карты размещаются в стеке, context.findRenderObject находит самый верхний стек. Этот стек охватывает весь экран, поэтому при преобразовании из глобальной в локальную позицию вы получаете ту же позицию.

Вы можете рассчитать правильное смещение для второго экрана следующим образом:

  var scrollOffset = controller.position.pixels;
  double position =  ((i * 175).toDouble() + 100 - scrollOffset) / 200;
  1. (i * 175): смещение каждой карты сверху.
  2. 100: разница в высоте между высотой карт на первом экране и второй экран (300 на первом и 200 на втором). Мы добавляем это в компенсировать разницу, поэтому положение карт будет то же самое.
  3. 200: высота карты на втором экране. Мы делим на это потому что

Перевод выражается в виде смещения, масштабированного до размера ребенка

Наконец, вам нужно предоставить scrollController для вашего SingleChildScrollView, чтобы получить смещение прокрутки. Без смещения прокрутки вы не можете рассчитать правильную позицию, если карты прокручиваются (т.е. карта 4 или карта 5)

Вот как должен выглядеть ваш HomePageState.

class HomePageState extends State<HomePage> {

  ScrollController controller = new ScrollController();


  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: Colors.indigo.shade200,
        floatingActionButton: Padding(
            padding: EdgeInsets.only(
                bottom: MediaQuery.of(context).size.height / 35),
            child: FloatingActionButton(
              child: Icon(Icons.add),
              backgroundColor: Colors.grey.shade200,
              foregroundColor: Colors.black,
              onPressed: () {},
            )),
        body: Stack(children: <Widget>[
          SingleChildScrollView(
            controller: controller,
              child: Padding(
                  padding: EdgeInsets.only(
                      bottom: MediaQuery.of(context).size.height / 11),
                  child:
                      Column(children: <Widget>[Stack(children: getCards())]))),
          Container(
              height: MediaQuery.of(context).size.height / 5,
              width: MediaQuery.of(context).size.width,
              decoration: BoxDecoration(
                  color: Colors.grey.shade200,
                  borderRadius:
                      BorderRadius.only(bottomLeft: Radius.circular(100.0))),
              child: Center(
                  child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                      children: <Widget>[
                    Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: <Widget>[
                        InkWell(
                            child: Padding(
                          padding: EdgeInsets.all(6.0),
                          child: Container(
                            height: 40.0,
                            width: 40.0,
                            decoration: BoxDecoration(
                                shape: BoxShape.circle,
                                border: Border.all(
                                    width: 2.0, color: Colors.pink.shade300)),
                            child: Icon(Icons.sentiment_satisfied),
                          ),
                        )),
                        Text('Tab1',
                            style: TextStyle(
                                fontSize: 10.0,
                                fontWeight: FontWeight.bold,
                                color: Colors.black38))
                      ],
                    ),
                    Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: <Widget>[
                        InkWell(
                            child: Padding(
                          padding: EdgeInsets.all(6.0),
                          child: Container(
                            height: 40.0,
                            width: 40.0,
                            decoration: BoxDecoration(
                                shape: BoxShape.circle,
                                border: Border.all(
                                    width: 2.0, color: Colors.pink.shade300)),
                            child: Icon(Icons.trending_up),
                          ),
                        )),
                        Text('Tab2',
                            style: TextStyle(
                                fontSize: 10.0,
                                fontWeight: FontWeight.bold,
                                color: Colors.black38))
                      ],
                    ),
                    Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: <Widget>[
                        InkWell(
                            child: Padding(
                          padding: EdgeInsets.all(6.0),
                          child: Container(
                            height: 40.0,
                            width: 40.0,
                            decoration: BoxDecoration(
                                shape: BoxShape.circle,
                                border: Border.all(
                                    width: 2.0, color: Colors.pink.shade300)),
                            child: Icon(Icons.favorite_border),
                          ),
                        )),
                        Text('Tab3',
                            style: TextStyle(
                                fontSize: 10.0,
                                fontWeight: FontWeight.bold,
                                color: Colors.black38))
                      ],
                    )
                  ])))
        ]));
  }

  List<Widget> getCards() {
    List<Widget> widgets = new List<Widget>();
    for (int i = 0; i < 5; i++) {
      widgets.add(GestureDetector(
        child: Container(
          margin: EdgeInsets.only(top: (i * 175).toDouble()),
          padding: EdgeInsets.all(60.0),
          height: 300.0,
          decoration: BoxDecoration(
            color: getColor(i),
            borderRadius: BorderRadius.only(bottomLeft: Radius.circular(100.0)),
          ),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.end,
            children: <Widget>[
              Center(
                      child: Text('Text ' + (i + 1).toString(),
                          maxLines: 2,
                          overflow: TextOverflow.ellipsis,
                          style: TextStyle(
                              color: Colors.white,
                              fontSize: 26.0,
                              fontWeight: FontWeight.bold)))
            ],
          ),
        ),
        onTapDown: (TapDownDetails details) async {
          var scrollOffset = controller.position.pixels;
          double position =  ((i * 175).toDouble() + 100 - scrollOffset) / 200;

          await Navigator.push(context, CustomPageRoute(builder: (context) {
            return SecondPage(index: i, startPosition: position);
          }));
        },
      ));
    }
    return widgets.reversed.toList();
  }
}
...