一尘不染

启用导航时在PageView中保留小部件状态

flutter

我在Flutter应用程式中遇到的情况比较复杂。我有一个可滑动的PageView主屏幕,其中显示3个子Widget:日历,消息,个人资料。

目前,我的问题是日历小部件。它是从initState()方法中动态填充的。

我设法解决了第一个问题,该问题是从一页滑动到另一页导致每次重新构建Calendar Widget的原因。

现在的问题是,当我点击“日历”列表中的某个项目时,我会打开详细视图。然后,当我关闭它时…一切仍然正常。但是,当我再次滑动时,将再次调用initState()方法,并重新构建List视图。我想防止这种情况并保留它的状态。有什么建议

这是本地代码。

class HomeStack extends StatefulWidget {

  final pages = <HomePages> [
    CalendarScreen(),
    MessagesScreen(),
    ProfileScreen(),
  ];

  @override
  _HomeStackState createState() => _HomeStackState();
}

class _HomeStackState extends State<HomeStack> with AutomaticKeepAliveClientMixin<HomeStack> {

  User user;

  @override
  bool get wantKeepAlive{
    return true;
  }

  @override
  void initState() {
    print("Init home");
    _getUser();
    super.initState();
  }

  void _getUser() async {
    User _user = await HomeBloc.getUserProfile();
    setState(() {
      user = _user;
    });
  }

  final PageController _pageController = PageController();
  int _selectedIndex = 0;

  void _onPageChanged(int index) {
    _selectedIndex = index;
  }

  void _navigationTapped(int index) {
    _pageController.animateToPage(
        index,
        duration: const Duration(milliseconds: 300),
        curve: Curves.ease
    );
  }

  GestureDetector _navBarItem({int pageIndex, IconData iconName, String title}) {
    return GestureDetector(
      child: HomeAppBarTitleItem(
          index: pageIndex,
          icon: iconName,
          title: title,
          controller: _pageController
      ),
      onTap: () => _navigationTapped(pageIndex),
    );
  }

  Widget _buildWidget() {
    if (user == null) {
      return Center(
        child: ProgressHud(imageSize: 70.0, progressSize: 70.0, strokeWidth: 5.0),
      );
    } else {
      return Scaffold(
        appBar: AppBar(
          title: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              _navBarItem(
                pageIndex: 0,
                iconName: Icons.calendar_today,
                title: AppLocalizations.of(context).calendarViewTitle,
              ),
              _navBarItem(
                pageIndex: 1,
                iconName: Icons.message,
                title: AppLocalizations.of(context).messagesViewTitle,
              ),
              _navBarItem(
                pageIndex: 2,
                iconName: Icons.face,
                title: AppLocalizations.of(context).profileViewTitle,
              ),
            ],
          ),
          backgroundColor: Colors.transparent,
          elevation: 0.0,
        ),
        backgroundColor: Colors.transparent,
        body: PageView(
          onPageChanged: (index) => _onPageChanged(index),
          controller: _pageController,
          children: widget.pages,
        ),
        floatingActionButton: widget.pages.elementAt(_selectedIndex).fabButton,
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      child: Stack(
        children: <Widget>[
          BackgroundGradient(),
          _buildWidget(),
        ],
      ),
      onWillPop: () async  {
        return true;
      },
    );
  }
}

和日历代码。

class CalendarScreen extends StatelessWidget implements HomePages {

  /// TODO: Prevent reloading
  /// when :
  /// 1) push detail view
  /// 2) swipe pageView
  /// 3) come back to calendar it reloads

  static const String routeName = "/calendar";

  static Color borderColor(EventPresence status) {
    switch (status) {
      case EventPresence.present:
        return CompanyColors.grass;
      case EventPresence.absent:
        return CompanyColors.asher;
      case EventPresence.pending:
        return CompanyColors.asher;
      default:
        return CompanyColors.asher;
    }
  }

  final FloatingActionButton fabButton = FloatingActionButton(
    onPressed: () {}, /// TODO: Add action to action button
    backgroundColor: CompanyColors.sky,
    foregroundColor: CompanyColors.snow,
    child: Icon(Icons.add),
  );

  @override
  Widget build(BuildContext context) {
    return CalendarProvider(
      child: CalendarList(),
    );
  }
}

class CalendarList extends StatefulWidget {
  @override
  _CalendarListState createState() => _CalendarListState();
}

class _CalendarListState extends State<CalendarList> with AutomaticKeepAliveClientMixin<CalendarList> {

  Events events;

  void _getEvents() async {
    Events _events = await CalendarBloc.getAllEvents();
    setState(() {
      events = _events;
    });
  }

  @override
  void initState() {
    _getEvents();
    super.initState();
  }

  @override
  bool get wantKeepAlive{
    return true;
  }

  Widget _displayBody() {
    if (events == null) {
      return ProgressHud(imageSize: 30.0, progressSize: 40.0, strokeWidth: 3.0);
    } else if(events.future.length == 0 && events.past.length == 0) return _emptyStateView();
    return EventsListView(events: events);
  }

  @override
  Widget build(BuildContext context) {
    return _displayBody();
  }

  Widget _emptyStateView() {
    return Center(
      child: Text("No data"),
    );
  }
}

class EventsListView extends StatefulWidget {

  final Events events;

  EventsListView({this.events});

  @override
  _EventsListViewState createState() => _EventsListViewState();
}

class _EventsListViewState extends State<EventsListView> {

  GlobalKey _pastEventsScrollViewKey = GlobalKey();
  GlobalKey _scrollViewKey = GlobalKey();

  double _opacity = 0.0;

  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      RenderSliverList renderSliver = _pastEventsScrollViewKey.currentContext.findRenderObject();
      setState(() {
        CustomScrollView scrollView = _scrollViewKey.currentContext.widget;
        scrollView.controller.jumpTo(renderSliver.geometry.scrollExtent);
        _opacity = 1.0;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(top: 8.0),
      child: AnimatedOpacity(
        opacity: _opacity,
        duration: Duration(milliseconds: 300),
        child: CustomScrollView(
          key: _scrollViewKey,
          controller: ScrollController(
            //initialScrollOffset: initialScrollOffset,
            keepScrollOffset: true,
          ),
          slivers: <Widget>[
            SliverList(
              key: _pastEventsScrollViewKey,
              delegate: SliverChildBuilderDelegate( (context, index) {
                Event event = widget.events.past[index];
                switch (event.type) {
                  case EventType.competition:
                    return CompetitionListItem(event: event);
                  case EventType.training:
                    return TrainingListItem(event: event);
                  case EventType.event:
                    return EventListItem(event: event);
                }
              },
                childCount: widget.events.past.length,
              ),
            ),
            SliverList(
              delegate: SliverChildBuilderDelegate( (context, index) {
                return Padding(
                  padding: EdgeInsets.only(top: 32.0, left: 16.0, right: 16.0, bottom: 16.0),
                  child: Text(
                    DateFormat.MMMMEEEEd().format(DateTime.now()),
                    style: Theme.of(context).textTheme.body2.copyWith(
                      color: CompanyColors.snow,
                    ),
                  ),
                );
              },
                childCount: 1,
              ),
            ),
            SliverList(
              delegate: SliverChildBuilderDelegate( (context, index) {
                Event event = widget.events.future[index];
                switch (event.type) {
                  case EventType.competition:
                    return CompetitionListItem(event: event);
                  case EventType.training:
                    return TrainingListItem(event: event);
                  case EventType.event:
                    return EventListItem(event: event);
                }
              },
                childCount: widget.events.future.length,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

阅读 292

收藏
2020-08-13

共1个答案

一尘不染

AutomaticKeepAliveClientMixin的文档中

///为[AutomaticKeepAlive]的客户提供了具有便捷方法的混合。与[State]子类一起使用。

///子类必须实现[wantKeepAlive],并且必须调用其[build]方法super.build(返回值将始终返回null,应将其忽略)。

因此,在您的代码中,在返回脚手架之前,只需调用super.build:

  Widget build(BuildContext context) {
    super.build(context);
    return Scaffold(...);
  }
2020-08-13