Flutter: сохранение состояния PageController (в PageView) при перестроении родительского виджета

Фон

Я пытаюсь реализовать складной контейнер, который сворачивается, когда ListView ниже прокручивается вверх (таким образом, сворачивается при прокрутке Listview). Для этой цели я использую DraggableScrollableSheet с AnimatedContainer, так что при прокрутке устанавливается новая высота для AnimatedContainer (вызывая коллапс ) .

В этом AnimatedContainer у меня есть PageView (обернутый SingleChildScrollView), так что пользователь может переключаться между различными виджетами карточек с помощью PageController.

Поскольку крах AnimatedContainer требует перестроения его дочерних элементов, я хочу сохранить состояние текущего выбранного карточного виджета при этих перестроениях (== изменение размера контейнера).

Проблема

Каким-то образом, когда AnimatedContainer перестраивается, PageController сбрасывается, и вместо текущего отображается первый выбранный карточный виджет.

Минимальный код проблемы

home_page.dart

      /// Actually [MyHomePage] is a TabItem for the actual Home Page
/// hence the [AutomaticKeepAliveClientMixin] is needed
class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with AutomaticKeepAliveClientMixin<MyHomePage> {
  double _appointmentHeightFrac = 3.0;

  bool _appointsCollapsed = false;
  // Seems to keep resetting when the container is collapsed . . .
  final PageController _controller = PageController(keepPage: true, );

  @override
  Widget build(BuildContext context) {
    super.build(context);

    return Stack(
      children: [
        AnimatedContainer(
          duration: const Duration(milliseconds: 75),
          height: MediaQuery.of(context).size.height / _appointmentHeightFrac,
          child: Card(
              color: Colors.blue,
              child: Center(
                  // RELEVANT WIDGET
                  child: UpcomingAppoint(isCollapsed: _appointsCollapsed, tileController: _controller)
              )
          ),
        ),
        DraggableScrollableSheet(
          initialChildSize: 0.5,
          minChildSize: 0.5,
          maxChildSize: 3 / 4,
          builder: (BuildContext context, ScrollController scrollController) {
            return NotificationListener<ScrollNotification>(
              onNotification: (ScrollNotification scrollNotification) {
                if (scrollController.position.userScrollDirection ==
                    ScrollDirection.reverse) {
                  setState(() {
                    _appointmentHeightFrac = 10.0;
                    _appointsCollapsed = true;
                  });
                  return true;
                } else if (scrollController.position.userScrollDirection ==
                    ScrollDirection.forward &&
                    scrollNotification.metrics.atEdge) {
                  setState(() {
                    _appointmentHeightFrac = 3.0;
                    _appointsCollapsed = false;
                  });
                  return true;
                }

                return false;
              },
              child: ListView.builder(
                  controller: scrollController,
                  itemCount: 7,
                  itemBuilder: (BuildContext context, int index) {
                    return ListTile(
                      title: Text('Item $index'),
                    );
                  }),
            );
          },
        ),
      ],
    );
  }

  @override
  bool get wantKeepAlive => true;
}

предстоящее_назначение.dart

      class UpcomingAppoint extends StatefulWidget {
  bool isCollapsed;
  PageController tileController;

  UpcomingAppoint({required this.isCollapsed, super.key, required this.tileController});

  @override
  State<UpcomingAppoint> createState() => _UpcomingAppointState();
}

class _UpcomingAppointState extends State<UpcomingAppoint> {

  @override
  Widget build(BuildContext context) {
    return (widget.isCollapsed)
        ? _SwippableCard(tileController: widget.tileController)
        : Column(
      children: [
        _SwippableCard(tileController: widget.tileController),
        const Text("Random bottom Text, that will disappear when collapsed")
      ],
    );
  }
}
      class _SwippableCardState extends State<_SwippableCard> with AutomaticKeepAliveClientMixin<_SwippableCard> {
  final pages = List.unmodifiable([
    const Center(child: Text("Tile Page 1")),
    const Center(child: Text("Tile Page 2"))
  ]);

  @override
  Widget build(BuildContext context) {
    super.build(context);

    return SingleChildScrollView(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[

          const SizedBox(height: 16),

          SizedBox(
            height: 100,
            child: PageView.builder(
              controller: widget.tileController,
              itemCount: pages.length,
              itemBuilder: (_, index) {
                return pages[index % pages.length];
              },
            ),
          ),
        ],
      ),
    );
  }

  @override
  bool get wantKeepAlive => true;
}

Пробные решения

Я попытался вытащить PageController вверх, поэтому он не воссоздан. Пробовал сделать его статичным. Пробовал использовать AutomaticKeepAliveClientMixin, так как он работает для моего TabView. Пробовал через LocalKeys (может неправильно?). Пытался использоватьsavePage: trueфлаг для PageController.

Диагностическая информация

      Flutter (Channel stable, 3.0.3, on Microsoft Windows [Version 10.0.22000.739], locale en-US)
    • Flutter version 3.0.3 at C:\Users\ahalm\.android\flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 676cefaaff (13 days ago), 2022-06-22 11:34:49 -0700
    • Engine revision ffe7b86a1e
    • Dart version 2.17.5
    • DevTools version 2.12.2

Я чувствую, что упускаю что-то очень тривиальное, но я не могу понять это сам. Любая помощь будет оценена по достоинству. Кроме того, если есть более элегантный способ сделать это (без чрезмерной инженерии с блоками или около того), то я также хотел бы это знать. Заранее спасибо!

0 ответов

Другие вопросы по тегам