Change AppBar color and text based on the page currently viewed
I have a TabBarView that navigates between pages in my application. Is there a way to change / override the text and color of the main AppBar on the currently viewed page, rather than creating an AppBar for each page separately?
This is how my pages are set up
My routes are defined in the main function like this:
routes: <String, WidgetBuilder>{
"/Home": (BuildContext context) => new first.Home(),
"/Support": (BuildContext context) => new second.Support(),
}
Tab class
class Tabs extends StatefulWidget {
@override
TabsState createState() => new TabsState();
}
class TabsState extends State<Tabs> with SingleTickerProviderStateMixin {
TabController controller;
@override
void initState() {
super.initState();
controller = new TabController(length: 5, vsync: this);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
centerTitle: true,
title: new Text('App'), backgroundColor: Colors.blue,
bottom: new TabBar(
controller: controller,
tabs: <Tab>[
new Tab (icon: new Icon(Icons.home), text: 'Home',),
new Tab (icon: new Icon(Icons.mail), text:'Support'),
]),
),
body: new TabBarView(
controller: controller,
children: <Widget>[
new first.Home(),
new second.Support(),
],
),
);
}
source to share
I modified this code to add support for text and color changes. I think,
https://flutter.io/catalog/samples/tabbed-app-bar/
Sorry for the ugliness for the code. All I did was change all classes to a stateful widget, add a setstate icon selector, change the widget so the onPressed callback appears
import 'package:flutter/material.dart';
class MainApp extends StatefulWidget {
MainApp({Key key, this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful,
// meaning that it has a State object (defined below) that contains
// fields that affect how it looks.
// This class is the configuration for the state. It holds the
// values (in this case the title) provided by the parent (in this
// case the App widget) and used by the build method of the State.
// Fields in a Widget subclass are always marked "final".
final String title;
@override
TabbedAppBarSample createState() => new TabbedAppBarSample();
}
class TabbedAppBarSample extends State<MainApp> {
Choice _choice;
initState(){
super.initState();
_choice = choices[0];
}
void _select(var c){
setState((){
_choice = c;
});
}
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new DefaultTabController(
length: choices.length,
child: new Scaffold(
appBar: new AppBar(
//dynamically create appbar colors
backgroundColor: new Color(_choice.color),
title: new Text(_choice.title),
bottom: new TabBar(
isScrollable: true,
tabs: choices.map((Choice choice) {
//change to iconbutton
return new IconButton(
icon: new Icon(choice.icon),
onPressed: (){_select(choice);},
);
}).toList(),
),
),
body:
new TabBarView(
children: choices.map((Choice choice) {
return new Padding(
padding: const EdgeInsets.all(16.0),
child: new ChoiceCard(choice: choice),
);
}).toList(),
),
),
),
);
}
}
class Choice {
const Choice({ this.title, this.icon, this.color});
final String title;
final IconData icon;
final num color;
}
const List<Choice> choices = const <Choice>[
const Choice(title: 'CAR', icon: Icons.directions_car, color: 0xFFE0F7FA),
const Choice(title: 'BICYCLE', icon: Icons.directions_bike, color: 0x00ff0000),
const Choice(title: 'BOAT', icon: Icons.directions_boat, color: 0xFF42A5F5),
const Choice(title: 'BUS', icon: Icons.directions_bus, color: 0x0),
const Choice(title: 'TRAIN', icon: Icons.directions_railway, color: 0xFFEFFFFF),
const Choice(title: 'WALK', icon: Icons.directions_walk, color: 0x0000ff00),
];
class ChoiceCard extends StatefulWidget {
ChoiceCard({Key key, this.choice}) : super(key: key);
final Choice choice;
@override
_ChoiceCard createState() => new _ChoiceCard();
}
class _ChoiceCard extends State<ChoiceCard> {
@override
Widget build(BuildContext context) {
final TextStyle textStyle = Theme.of(context).textTheme.display1;
return new Card(
color: Colors.white,
child: new Center(
child: new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Icon(widget.choice.icon, size: 128.0, color: textStyle.color),
new Text(widget.choice.title, style: textStyle),
],
),
),
);
}
}
void main() {
runApp(new MainApp());
}
I'm a little annoyed because my code above is similar to the actual answer to the need. The only difference between my code above and what the operator needs is that I added the changes to the tabcontroller instead of the button itself
Here is the code
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget{
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Nothing',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new Tabs(),
);
}
}
class Tabs extends StatefulWidget {
@override
TabsState createState() => new TabsState();
}
class TabsState extends State<Tabs> with SingleTickerProviderStateMixin {
TabController controller;
//create internal state
Choice _choice;
@override
void initState() {
super.initState();
//try to make the length to
controller = new TabController(length: 5, vsync: this);
//add listener to add change index callback
//https://docs.flutter.io/flutter/material/TabController-class.html
controller.addListener(_select);
_choice = choices[0];
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
void _select(){
setState((){
_choice = choices[controller.index];
});
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
centerTitle: true,
title: new Text(_choice.title), backgroundColor: new Color(_choice.color),
bottom: new TabBar(
controller: controller,
tabs: <Tab>[
new Tab( icon: new Icon(choices[0].icon), text: 'Home',),
new Tab (icon: new Icon(choices[1].icon), text:'Support'),
]),
),
body: new TabBarView(
controller: controller,
children: <Widget>[
//dummy page
new MyHomePage(),
new Center( child: new Text('dummy page 2')),
],
),
);
}
}
class Choice {
const Choice({ this.title, this.icon, this.color});
final String title;
final IconData icon;
final num color;
}
//create a list
const List<Choice> choices = const <Choice>[
const Choice(title: 'Home', icon: Icons.home, color: 0x0),
const Choice(title: 'Support', icon: Icons.mail, color: 0xFF42A5F5),
];
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return new Center(
child: new Text('dummy page'),
);
}
}
source to share
If you use PageView
instead TabBarView
, you can specify a function onPageChanged
that allows you to change the state, so rebuild the widget.
Here's some code I'm working on, the title has been changed in the app bar, but the concept is fundamentally the same:
// Copyright 2017 <Abhi Agarwal>
// Refer to LICENSE
// Dart Imports
// Flutter Imports
import 'package:flutter/material.dart';
// Package Imports
import 'package:shared_preferences/shared_preferences.dart'
show SharedPreferences;
// Local Imports
import '../calendar/calendar_view.dart' show CalendarView;
import '../error/error_screen.dart' show ErrorScreen;
import '../homework/homework_view.dart' show HomeworkView;
import '../loading/loading_screen.dart' show LoadingScreen;
import 'page.dart' show Page;
class MainView extends StatefulWidget {
MainView({Key key, this.initialIndex = 0, SharedPreferences prefs})
: pages = _makePagesList(prefs),
super(key: key);
final int initialIndex;
final List<Page> pages;
static List<Page> _makePagesList(SharedPreferences prefs) => <Page>[
CalendarView.page(),
new Page(
page: new ErrorScreen(error: "Hello World"),
title: "Schedule",
iconData: Icons.schedule,
),
HomeworkView.page(),
new Page(
page: new LoadingScreen(),
title: "Settings",
iconData: Icons.settings,
),
];
@override
_MainViewState createState() => new _MainViewState();
}
class _MainViewState extends State<MainView> {
PageController _controller;
int _index;
@override
void initState() {
super.initState();
_controller = new PageController(initialPage: widget.initialIndex);
_index = widget.initialIndex;
}
@override
void dispose() {
super.dispose();
_controller.dispose();
}
void _handlePageChange(int index) => setState(() => _index = index);
void _navigateToPage(int index) => _controller.animateToPage(
index,
duration: const Duration(milliseconds: 300),
curve: Curves.ease,
);
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(
widget.pages[_index].title,
style: new TextStyle(
fontFamily: Theme.of(context).textTheme.title.fontFamily,
),
),
backgroundColor: Theme.of(context).primaryColor,
),
bottomNavigationBar: new BottomNavigationBar(
type: BottomNavigationBarType.shifting,
items: widget.pages
.map((Page page) => new BottomNavigationBarItem(
icon: new Icon(
page.iconData,
color: Theme.of(context).primaryColor,
),
title: new Text(
page.title,
style: new TextStyle(
color: Theme.of(context).primaryColor,
),
),
backgroundColor: Theme.of(context).canvasColor,
))
.toList(),
onTap: _navigateToPage,
currentIndex: _index,
),
floatingActionButton: widget.pages[_index].useFab
? new FloatingActionButton(
tooltip: widget.pages[_index].tooltip,
child: widget.pages[_index].fab,
onPressed: () => widget.pages[_index].onPressed(context),
)
: null,
body: new PageView(
controller: _controller,
children: widget.pages.map((Page page) => page.page).toList(),
onPageChanged: _handlePageChange,
),
);
}
}
source to share