首页 > 解决方案 > StreamProvider 更新时如何让 Navigator 保持在活动路径上?

问题描述

在下面的代码中,我创建了一个自定义导航器,它是 StreamProvider 的子级。我的问题是每次从 Firestore 接收快照时,StreamProvider 都会重置 Navigator 路由。如何在保持当前路由的同时使用流中的新值更新 UI?

此外,第二个代码块中的 ListView 会重置流中更新的滚动位置。如何保留滚动位置,同时仍使用新值更新 UI?

class _HomeState extends State<Home> {
  @override
  initState() {
    super.initState();
  }

  int _selectedIndex = 0;

  Future<void> _onItemTapped(int index) async {
    setState(() {
      _selectedIndex = index;
      print(index);
    });
  }

  @override
  Widget build(BuildContext context) {
    final FixturesPage fixturesPage = new FixturesPage();
    final ProfilePage profilePage = new ProfilePage();

    FirebaseUser user = Provider.of<FirebaseUser>(context);

    List<Widget> pages = [
      fixturesPage,
      profilePage,
    ];

    return MultiProvider(
      providers: [
        StreamProvider<int>.value(
            value: DatabaseService(userId: user.uid).totalPoints),
        StreamProvider<List<Bet>>.value(
            value: BettingService(userId: user.uid).getBetList)
      ],
      child: Scaffold(
        appBar: AppBar(
            elevation: 0.0, backgroundColor: darkShade5, title: AppBarTitle()),
        body: Container(
          decoration: BoxDecoration(
            color: darkShade4,
          ),
          child: pages[_selectedIndex],
        ),
        bottomNavigationBar: Container(
          child: BottomNavigationBar(
            type: BottomNavigationBarType.fixed,
            backgroundColor: darkShade7,
            selectedFontSize: 10,
            unselectedFontSize: 10,
            elevation: 0,
            unselectedIconTheme:
                IconThemeData(color: Colors.white.withOpacity(0.9)),
            selectedIconTheme: IconThemeData(color: mainColor),
            unselectedLabelStyle: navigationBarStyle.copyWith(
                color: Colors.white.withOpacity(0.9)),
            selectedLabelStyle: navigationBarStyle.copyWith(color: mainColor),
            items: [
              BottomNavigationBarItem(
                  icon: Container(
                    height: 20,
                    width: 20,
                    child: Image(
                      image: AssetImage("lib/images/HomeIcon.png"),
                      color: _selectedIndex == 0
                          ? mainColor
                          : Colors.white.withOpacity(0.9),
                    ),
                  ),
                  title: SizedBox()),
              BottomNavigationBarItem(
                  icon: Container(
                    height: 20,
                    width: 20,
                    child: Image(
                      image: AssetImage("lib/images/ProfileIcon.png"),
                      color: _selectedIndex == 1
                          ? mainColor
                          : Colors.white.withOpacity(0.9),
                    ),
                  ),
                  title: SizedBox()),
            ],
            currentIndex: _selectedIndex,
            selectedItemColor: mainColor,
            unselectedItemColor: Colors.white.withOpacity(0.3),
            onTap: _onItemTapped,
          ),
        ),
      ),
    );
  }
}
class _FixturesPageState extends State<FixturesPage> {
  int selectedTap = 0;

  Function setSelectedTap(int tap) {
    setState(() {
      selectedTap = tap;
    });

    return null;
  }

  Widget selectedSport = FavoritesTap();

  Function setSelectedSport(Widget newSelectedSport) {
    setState(() {
      selectedSport = newSelectedSport;
    });

    return null;
  }

  ScrollController listViewController =
      ScrollController(keepScrollOffset: true);

  @override
  Widget build(BuildContext context) {
    final GlobalKey<NavigatorState> navigatorKey = GlobalKey();
    List<dynamic> matches = Provider.of<List<SoccerMatch>>(context);

    return Navigator(
      key: navigatorKey,
      initialRoute: "/",
      onGenerateRoute: (routeSettings) {
        return MaterialPageRoute(
          builder: (context) => Container(
              color: darkShade6,
              child: Column(
                children: <Widget>[
                  //CalendarBar(),
                  Expanded(
                    child: ListView(
                      key: PageStorageKey('myListView'),
                      controller: listViewController,
                      children: <Widget>[
                        SizedBox(height: 30),
                        LiveSection(eventList: matches),
                        SizedBox(height: 20),
                        UpcomingSection(eventList: matches),
                        SizedBox(height: 20),
                        ResultsSection(eventList: matches)
                      ],
                    ),
                  )
                ],
              )),
        );
      },
    );
  }
}

标签: flutterdart

解决方案


显然,我通过将 ListView 与 Provider 和 ScrollController 一起移动到它自己的小部件中来解决了这两个问题:

class _FixturesPageState extends State<FixturesPage> {
  int selectedTap = 0;

  Function setSelectedTap(int tap) {
    setState(() {
      selectedTap = tap;
    });

    return null;
  }

  Widget selectedSport = FavoritesTap();

  Function setSelectedSport(Widget newSelectedSport) {
    setState(() {
      selectedSport = newSelectedSport;
    });

    return null;
  }

  @override
  Widget build(BuildContext context) {
    final GlobalKey<NavigatorState> navigatorKey = GlobalKey();

    return Navigator(
      key: navigatorKey,
      initialRoute: "/",
      onGenerateRoute: (routeSettings) {
        return MaterialPageRoute(
          builder: (context) => Container(
              color: darkShade6,
              child: Column(
                children: <Widget>[
                  //CalendarBar(),
                  Expanded(child: FixturesList())
                ],
              )),
        );
      },
    );
  }
}

class FixturesList extends StatelessWidget {
  final ScrollController listViewController =
      ScrollController(keepScrollOffset: true);
  @override
  Widget build(BuildContext context) {
    List<dynamic> matches = Provider.of<List<SoccerMatch>>(context);

    return ListView(
      key: PageStorageKey('myListView'),
      controller: listViewController,
      children: <Widget>[
        SizedBox(height: 30),
        LiveSection(eventList: matches),
        SizedBox(height: 20),
        UpcomingSection(eventList: matches),
        SizedBox(height: 20),
        ResultsSection(eventList: matches)
      ],
    );
  }
}


推荐阅读