我正在尝试构建一个bottomNavigationBar
,其中每个tabItem将保持其自己的导航状态。
我有5个导航项目:enum TabItem { top_stories, topics, special_reports, more }
。对于第一个,我使用HomeScreen
小部件显示文章列表。可以单击每篇文章以显示其全部内容。对于第二个tabItem(主题),我想显示一个不同的项目列表,但是现在我使用TopicScreen
小部件来显示一个简单的Text
字段。
在我的NewsApp
中,我为每个tabItem使用Stack
类周围的Offstage
和Navigator
小部件。
对于第一个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'),
),
);
}
}