启用导航时在PageView中保留小部件状态 [英] Preserve Widget State in PageView while enabling Navigation

查看:41
本文介绍了启用导航时在PageView中保留小部件状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在Flutter应用程序中遇到了一个相当复杂的情况. 我有一个可滑动的PageView主屏幕,其中显示3个子小部件:日历,消息,个人资料.

I have a rather complex situation in a Flutter App. I have a Home screen that is a swipable PageView,that displays 3 child Widgets : Calendar, Messages, Profile.

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

My issue at the moment is with the Calendar Widget. It is populated dynamically from the initState() method.

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

I managed to fix a first issue that came from swiping from one page to another that caused rebuilding the Calendar Widget every time.

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

My issue now is when I tap an item in the Calendar list, I open the detail view. Then, when I close it… all is still OK. However, when I swipe again the initState() method is called once more and the List view is rebuilt. I would like to prevent that and preserve it's state. any suggestions ?

这是本地代码.

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,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

推荐答案

来自 AutomaticKeepAliveClientMixin:

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

/// A mixin with convenience methods for clients of [AutomaticKeepAlive]. Used with [State] subclasses.

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

/// Subclasses must implement [wantKeepAlive], and their [build] methods must call super.build (the return value will always return null, and should be ignored).

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

So in your code, before you return the Scaffold just call super.build:

  Widget build(BuildContext context) {
    super.build(context);
    return Scaffold(...);
  }

这篇关于启用导航时在PageView中保留小部件状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆