Reputation: 2103
The Flutter Gallery example of BottomNavigationBar
uses a Stack
of FadeTransitions
in the body of the Scaffold
.
I feel it would be cleaner (and easier to animate) if we could switch pages by using a Navigator
.
Are there any examples of this?
Upvotes: 49
Views: 70981
Reputation: 15
code example here :
last updated code
int _selectedIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Offstage(
offstage: _selectedIndex != 0,
child: TickerMode(
enabled: _selectedIndex == 0,
child: MaterialApp(
debugShowCheckedModeBanner: false, home:
NavigationBarTest()),
),
),
Offstage(
offstage: _selectedIndex != 1,
child: TickerMode(
enabled: _selectedIndex == 1,
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: NavigationBarTest2()),
),
),
Offstage(
offstage: _selectedIndex != 2,
child: TickerMode(
enabled: _selectedIndex == 2,
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: NavigationBarTest3()),
),
),
],
),
bottomNavigation_bar code ex:
bottomNavigationBar: NavigationBar(
destinations: const [
NavigationDestination(icon: Icon(Icons.home), label: 'home'),
NavigationDestination(icon: Icon(Icons.settings), label: 'setting'),
NavigationDestination(icon: Icon(Icons.person), label: 'profile')
],
selectedIndex: _selectedIndex,
onDestinationSelected: (int index) => setState(() {
_selectedIndex = index;
}),
animationDuration: const Duration(seconds: 1),
),
NavigationBarTest
class NavigationBarTest extends StatelessWidget {
Widget build(BuildContext context) {
return Container(
color: Colors.amber,
);
}
}
class NavigationBarTest2 extends StatelessWidget {
Widget build(BuildContext context) {
return Container(
color: Colors.red,
);
}
}
class NavigationBarTest3 extends StatelessWidget {
Widget build(BuildContext context) {
return Container(
color: Colors.teal,
);
}
}
Upvotes: 0
Reputation: 20369
Glad You asked, I experimented with this a couple of months back and tried to simplify this through a blog post. I won't be able to post the complete code here since it is pretty long, But I can certainly link all the resources to clarify it.
Here's the resulting output of what the article helps you build
Upvotes: 2
Reputation: 1567
This is the code I am using in my project. If you try to avoid page viewer so you can try this
import 'package:flutter/material.dart';
class Dashboard extends StatefulWidget {
const Dashboard({Key? key}) : super(key: key);
@override
State<Dashboard> createState() => _DashboardState();
}
class _DashboardState extends State<Dashboard> {
int _selectedIndex = 0;
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Sample'),
),
body: SingleChildScrollView(
child: Column(
children: [
if (_selectedIndex == 0)
// you can call custom widget here
Column(
children: const [
Text("0"),
],
)
else if (_selectedIndex == 1)
Column(
children: const [
Text("1"),
],
)
else
Column(
children: const [
Text("2"),
],
),
],
),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.headphones),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Business',
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'School',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
unselectedItemColor: Colors.grey,
onTap: _onItemTapped,
),
);
}
}
Happy Coding
Upvotes: -1
Reputation: 1565
Here is an example how you can use Navigator with BottomNavigationBar to navigate different screen.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// This navigator state will be used to navigate different pages
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
int _currentTabIndex = 0;
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Navigator(key: _navigatorKey, onGenerateRoute: generateRoute),
bottomNavigationBar: _bottomNavigationBar(),
),
);
}
Widget _bottomNavigationBar() {
return BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text("Home"),
),
BottomNavigationBarItem(
icon: Icon(Icons.account_circle), title: Text("Account")),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
title: Text("Settings"),
)
],
onTap: _onTap,
currentIndex: _currentTabIndex,
);
}
_onTap(int tabIndex) {
switch (tabIndex) {
case 0:
_navigatorKey.currentState.pushReplacementNamed("Home");
break;
case 1:
_navigatorKey.currentState.pushReplacementNamed("Account");
break;
case 2:
_navigatorKey.currentState.pushReplacementNamed("Settings");
break;
}
setState(() {
_currentTabIndex = tabIndex;
});
}
Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case "Account":
return MaterialPageRoute(builder: (context) => Container(color: Colors.blue,child: Center(child: Text("Account"))));
case "Settings":
return MaterialPageRoute(builder: (context) => Container(color: Colors.green,child: Center(child: Text("Settings"))));
default:
return MaterialPageRoute(builder: (context) => Container(color: Colors.white,child: Center(child: Text("Home"))));
}
}
}
Upvotes: 16
Reputation: 268384
Output:
Code:
int _index = 0;
@override
Widget build(BuildContext context) {
Widget child;
switch (_index) {
case 0:
child = FlutterLogo();
break;
case 1:
child = FlutterLogo(colors: Colors.orange);
break;
case 2:
child = FlutterLogo(colors: Colors.red);
break;
}
return Scaffold(
body: SizedBox.expand(child: child),
bottomNavigationBar: BottomNavigationBar(
onTap: (newIndex) => setState(() => _index = newIndex),
currentIndex: _index,
items: [
BottomNavigationBarItem(icon: Icon(Icons.looks_one), title: Text("Blue")),
BottomNavigationBarItem(icon: Icon(Icons.looks_two), title: Text("Orange")),
BottomNavigationBarItem(icon: Icon(Icons.looks_3), title: Text("Red")),
],
),
);
}
Upvotes: 22
Reputation: 197
Navigator.of(context).pushNamedAndRemoveUntil(
routes[value], (route) => true);
I had to use true to enable back button.
NB: I was using Navigator.pushNamed()
for navigation.
Upvotes: 0
Reputation: 3253
int index = 0;
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Stack(
children: <Widget>[
new Offstage(
offstage: index != 0,
child: new TickerMode(
enabled: index == 0,
child: new MaterialApp(home: new YourLeftPage()),
),
),
new Offstage(
offstage: index != 1,
child: new TickerMode(
enabled: index == 1,
child: new MaterialApp(home: new YourRightPage()),
),
),
],
),
bottomNavigationBar: new BottomNavigationBar(
currentIndex: index,
onTap: (int index) { setState((){ this.index = index; }); },
items: <BottomNavigationBarItem>[
new BottomNavigationBarItem(
icon: new Icon(Icons.home),
title: new Text("Left"),
),
new BottomNavigationBarItem(
icon: new Icon(Icons.search),
title: new Text("Right"),
),
],
),
);
}
You should keep each page by Stack
to keep their state.
Offstage
stops painting, TickerMode
stops animation.
MaterialApp
includes Navigator
.
Upvotes: 60
Reputation: 6961
Here is example:
int _currentIndex = 0;
Route<Null> _getRoute(RouteSettings settings) {
final initialSettings = new RouteSettings(
name: settings.name,
isInitialRoute: true);
return new MaterialPageRoute<Null>(
settings: initialSettings,
builder: (context) =>
new Scaffold(
body: new Center(
child: new Container(
height: 200.0,
width: 200.0,
child: new Column(children: <Widget>[
new Text(settings.name),
new FlatButton(onPressed: () =>
Navigator.of(context).pushNamed(
"${settings.name}/next"), child: new Text("push")),
],
))
),
bottomNavigationBar: new BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (value) {
final routes = ["/list", "/map"];
_currentIndex = value;
Navigator.of(context).pushNamedAndRemoveUntil(
routes[value], (route) => false);
},
items: [
new BottomNavigationBarItem(
icon: new Icon(Icons.list), title: new Text("List")),
new BottomNavigationBarItem(
icon: new Icon(Icons.map), title: new Text("Map")),
]),
));
}
@override
Widget build(BuildContext context) =>
new MaterialApp(
initialRoute: "/list",
onGenerateRoute: _getRoute,
theme: new ThemeData(
primarySwatch: Colors.blue,
),
);
You can set isInitialRoute to true and pass it to MaterialPageRoute. It will remove pop animation.
And to remove old routes you can use pushNamedAndRemoveUntil
Navigator.of(context).pushNamedAndRemoveUntil(routes[value], (route) => false);
To set current page you can have a variable in your state _currentIndex
and assign it to BottomNavigationBar:
Upvotes: 3