使用offStageNavigator小部件时BottomNavigationBar不显示当前选项卡

时间:2019-10-09 09:58:56

标签: flutter dart

我正在尝试构建一个bottomNavigationBar,其中每个tabItem将保持其自己的导航状态。

我有5个导航项目:enum TabItem { top_stories, topics, special_reports, more }。对于第一个,我使用HomeScreen小部件显示文章列表。可以单击每篇文章以显示其全部内容。对于第二个tabItem(主题),我想显示一个不同的项目列表,但是现在我使用TopicScreen小部件来显示一个简单的Text字段。

在我的NewsApp中,我为每个tabItem使用Stack类周围的OffstageNavigator小部件。

对于第一个tabItem,一切正常。但是,当我单击topics时,看不到各个TopicScreen小部件的内容,但文章的整个列表将再次显示。奇怪的是,此列表似乎是为该tabItem从头开始再次创建的。我可以为每个tabItem完全选择不同的文章,浏览所有bottomNavOptions,然后该应用程序将“记住”我的选择。

在代码中:

NewsApp.dart

import 'package:flutter/material.dart';
import 'package:news_app/navigation/bottom_navigation.dart';
import 'package:news_app/navigation/tab_navigator.dart';

class NewsApp extends StatefulWidget {
//  NewsApp({Key key}) : super(key: key);

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

class _NewsAppState extends State<NewsApp> {
  /// Give a unique key to each one of the bottom navigation tab items
  Map<TabItem, GlobalKey<NavigatorState>> _navigatorKeys = {
    TabItem.top_stories: GlobalKey<NavigatorState>(),
    TabItem.topics: GlobalKey<NavigatorState>(),
    TabItem.special_reports: GlobalKey<NavigatorState>(),
    TabItem.more: GlobalKey<NavigatorState>(),
  };
  TabItem currentTab = TabItem.top_stories;

  /// This function is passed to the onTap callback upon clicking on a [tabItem].
  void _selectTab(TabItem tabItem) {
    setState(() {
      currentTab = tabItem;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('AppNews.gr'),
        elevation: 0.1,
      ),

      /// Making tab navigation stateful. Stack all tab items, fade-in the selected
      /// view and fade out the rest (unselected - _currentTab != tabItem). The
      /// faded out views are laid out in the widget tree but not painted and are
      /// modeled with the offstage property.
      body: Stack(children: <Widget>[
        _buildOffStageNavigator(TabItem.top_stories),
        _buildOffStageNavigator(TabItem.topics),
        _buildOffStageNavigator(TabItem.special_reports),
        _buildOffStageNavigator(TabItem.more)
      ]),
      bottomNavigationBar:
          BottomNavigation(currentTab: currentTab, onSelectTab: _selectTab),
    );
  }

  /// This function wraps each [tabItem] into each own [TabNavigator]
  Widget _buildOffStageNavigator(TabItem tabItem) {
    return Offstage(
        offstage: currentTab != tabItem,
        child: TabNavigator(
          navigatorKey: _navigatorKeys[tabItem],
          tabItem: tabItem,
        ));
  }
}

TabNavigator.dart

import 'package:flutter/material.dart';
import 'package:news_app/navigation/routes.dart';
import 'package:news_app/models/articles.dart';
import 'package:news_app/navigation/bottom_navigation.dart';
import 'package:news_app/screens/Home/home_screen.dart';
import 'package:news_app/screens/Detail/detail_screen.dart';
import 'package:news_app/screens/Topics/topic_screen.dart';

/// A navigator class used to perform routing and state management among different
/// [tabItem]s. Uses a unique [navigatorKey] to track the state of the
/// [TabNavigator] object across the app.
class TabNavigator extends StatelessWidget {
  final GlobalKey<NavigatorState> navigatorKey;
  final TabItem tabItem;

  TabNavigator({this.navigatorKey, this.tabItem});

  /// A method used to push a detail route in a specific [context].
  void _push(BuildContext context, {Article article}) {
    var routeBuilder = _routeBuilder(context, specArticle: article);

    Navigator.push(
        context,
        MaterialPageRoute(
            builder: (context) => routeBuilder[Routes.detail](context)));
  }

  /// A method to be passed to the route generator callback (onGenerateRoute)
  /// when the app is navigated to a named route.
  Map<String, WidgetBuilder> _routeBuilder(BuildContext context,
      {Article specArticle}) {
    return {
      ///The home screen containing all articles('/')
      Routes.home: (context) => HomeScreen(
            onPush: (specArticle) => _push(context, article: specArticle),
          ),

      ///The detail screen of a specific article('/detail')
      Routes.detail: (context) => DetailScreen(article: specArticle),

      ///The topics screen of all the available topics('/topics')
      Routes.topics: (context) => TopicScreen(), /// <-- THIS DOESN'T SEEM TO WORK. 
    };
  }

  @override
  Widget build(BuildContext context) {
    final routeBuilders = _routeBuilder(context);

    return Navigator(
        key: navigatorKey,
        initialRoute: Routes.home,
        onGenerateRoute: (RouteSettings routeSettings) {
          return MaterialPageRoute(
              settings: routeSettings,
              builder: (context) => routeBuilders[routeSettings.name](context));
        });
  }
}

BottomNavigation.dart

import 'package:flutter/material.dart';
import 'package:news_app/screens/Home/Style/home_style.dart';

/// An enum struct of all the different bottom navigation items.
enum TabItem { top_stories, topics, special_reports, more }

/// A class built on BottomNavigationBar widget used to navigate among the app's
/// [tabItem]s. Defines static const Map<[tabItem], String>s to associate a tab
/// with a material icon.
class BottomNavigation extends StatelessWidget {
  final TabItem currentTab;
  final ValueChanged<TabItem> onSelectTab;

  static const Map<TabItem, String> tabName = {
    TabItem.top_stories: 'Top Stories',
    TabItem.topics: 'Topics',
    TabItem.special_reports: 'Special Reports',
    TabItem.more: 'More',
  };

  static const Map<TabItem, Icon> tabIcon = {
    TabItem.top_stories: Icon(Icons.subject),
    TabItem.topics: Icon(Icons.format_list_bulleted),
    TabItem.special_reports: Icon(Icons.filter_none),
    TabItem.more: Icon(Icons.more_horiz),
  };

  BottomNavigation({this.currentTab, this.onSelectTab});

  @override
  Widget build(BuildContext context) {
    return BottomNavigationBar(
      ///Fixed type is the default when there are less than four items.
      ///The selected item is rendered with the selectedItemColor if it's non-null,
      ///otherwise the theme's ThemeData.primaryColor is used.
//      type: BottomNavigationBarType.shifting,
      items: [
        _buildItem(
            TabItem.top_stories, BottomNavigation.tabIcon[TabItem.top_stories]),
        _buildItem(TabItem.topics, BottomNavigation.tabIcon[TabItem.topics]),
        _buildItem(TabItem.special_reports,
            BottomNavigation.tabIcon[TabItem.special_reports]),
        _buildItem(TabItem.more, BottomNavigation.tabIcon[TabItem.more]),
      ],
      onTap: (index) => onSelectTab(
        TabItem.values[index],
      ),
      selectedItemColor: bottomNavBarItemsColor,
    );
  }

  BottomNavigationBarItem _buildItem(TabItem tabItem, Icon tabIcon) {
    String text = BottomNavigation.tabName[tabItem];
    return BottomNavigationBarItem(
        icon: tabIcon,
        title: Text(text),
        backgroundColor: bottomNavBarBackgroundColor);
  }
}

HomeScreen.dart

import 'package:flutter/material.dart';
import 'package:news_app/models/articles.dart';
import 'package:news_app/models/tags.dart';
import 'package:news_app/screens/Home/Style/home_style.dart';
import 'package:news_app/widgets/article_card.dart';

/// The home screen widget that shows the list of [articles]
class HomeScreen extends StatefulWidget {
  final ValueChanged onPush;

  HomeScreen({Key key, this.onPush}) : super(key: key);

  @override
  _HomeScreenState createState() => _HomeScreenState(onPushCard: onPush);
}

class _HomeScreenState extends State<HomeScreen> {
  List articles;
  final ValueChanged onPushCard;

  _HomeScreenState({this.onPushCard});

  /// Dummy fetch the list of articles (will be swapped out for the api version)
  @override
  void initState() {
    articles = getDummyArticles();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
        //TODO-me: Research if this is actually needed
        child: ListView.builder(
            //shrinkWrap: true, //TODO-me: Test this
            itemCount: articles.length, //TODO-me: Remove to scroll infinitely
            itemBuilder: (BuildContext context, int index) {
              return ArticleCard(
                  cardElevation: articleTileElevation,
                  cardMargin: articleTileMargin,
                  cardDecoration: articleTileDecoration,
                  cardTilePadding: articleTilePadding,
                  cardTextTitleStyle: articleTileTitleStyle,
                  cardTextSubHeaderStyle: articleTileSubHeaderStyle,
                  cardArticle: articles[index],
                  pushCardAction: onPushCard);
            }));
  }

  @override
  void dispose() {
    super.dispose();
  }
}

//TODO-me: Dummy list of articles. 
List getDummyArticles() {
  return[Article(
    title:
        'Libra cryptocurrency not out to replace existing money: project head',
    subHeader:
        "The Facebook-conceived (FB.O) Libra cryptocurrency, to be launched by a Geneva-based association next year, is not intended to replace existing currencies, Libra Association Managing Director Bertrand Perez said on Friday.",
    journalistName: 'Susan Cornwell, Makini Brice',
    content:
        """The Facebook-conceived (FB.O) Libra cryptocurrency, to be launched by a Geneve.""",
    timeOfPublish: "26-09-19 10:14",
    tag: [
      Tag(tag: 'Fintech'),
      Tag(tag: 'Cryptocurrency'),
      Tag(tag: 'Facebook')
    ])]
}

TopicScreen.dart

import 'package:flutter/material.dart';

class TopicScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        child: Text('Hello Topics'),
      ),
    );
  }
}

0 个答案:

没有答案