5

I have an AppBar in main.dart and I want to defined it as primary on it's child, But I want to change the title of AppBar itself when I'm on child's page, how can i do that properly?

void main() => runApp(MyApp()); class MyApp extends StatelessWidget{ @override Widget build(BuildContext context) { return MaterialApp( title: "Flutter App", theme: ThemeData( primaryColor: Colors.cyan, brightness: Brightness.dark ), home: Scaffold( appBar: AppBar( title: Text("Main Dart"), ), body: HomeScreen(), ), routes: <String, WidgetBuilder>{ '/homeScreen': (buildContext)=>HomeScreen(), '/second': (buildContext)=>Second() }, ); } } //HomeScreen or Second Widget on different dart file class HomeScreen extends StatelessWidget { @override Widget build(BuildContext context) { //here I want to change the title of Main Dart to HomeScreen return Container( child: Center( child: FlatButton( child: new Text("Home screen"), onPressed: (){ Route route = MaterialPageRoute(builder: (context) => Second()); Navigator.push(context, route); }, ), ), ); } } 

or I need to put Scaffold(appBar:AppBar(...), ...) in every screen? it is the best approach?

12
  • can you post Second class , are you using Scaffold in every route Commented Apr 18, 2019 at 4:05
  • @Doc i want to avoid Scaffold on every route, so my second route same as HomeScreen Commented Apr 18, 2019 at 4:06
  • is it completely unavoidable to have scafffolds ? Commented Apr 18, 2019 at 4:08
  • 1
    @doc the main question in my mind is "why i need to put Scaffold on every page just to change the title of appBar?" | it's just my question as a beginner flutter dev, and any suggest are welcome Commented Apr 18, 2019 at 4:12
  • seems like a legit doubt; looking into it. Commented Apr 18, 2019 at 4:17

5 Answers 5

6

Have a BLoC for app properties in app_properties_bloc.dart

final appBloc = AppPropertiesBloc(); class AppPropertiesBloc{ StreamController<String> _title = StreamController<String>(); Stream<String> get titleStream => _title.stream; updateTitle(String newTitle){ _title.sink.add(newTitle); } dispose() { _title.close(); } } 

Use stream builder in AppBar like this:

AppBar( title: StreamBuilder<Object>( stream: appBloc.titleStream, initialData: "Main Dart", builder: (context, snapshot) { return Text(snapshot.data); } ), ), 

Use this to update title on button's onPressed()

onPressed: () { appBloc.updateTitle('new title'); }, 
Sign up to request clarification or add additional context in comments.

6 Comments

why does it look like killing a fly with a machine gun??
too much for a simple task. and I thought Flutter will reduce code :(
This is because you just have one property that changes like this and for that one property you don't feel like it's worth it.. but suppose you have multiple properties like this then you won't feel the same.. Using StreamBuilder to update the data is efficient, as it will build only the widget that needs an update..
yes i thought the same thing, it is nice if there are a bunch of things but looks an overkill for just one thing.
@KalpeshKundanani will it work in case of Navigator.of(context).pop();?
|
2

Just in case you are changing only the title of Scaffold then this will work.

I am creating a DefaultScaffold with the title each screen provides. Here the code will show the MainPage and two other pages which have the same AppBar with changed titles.

void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp(initialRoute: 'home', routes: <String, WidgetBuilder>{ 'home': (context) => SOMain(), '/secondPage': (context) => DefaultScaffold("Second Screen", SOSecond()), '/thirdPage': (context) => DefaultScaffold("Third Screen", SOThird()), }); } } class DefaultScaffold extends StatelessWidget { String title; Widget body; DefaultScaffold(this.title, this.body); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(title), ), body: body, ); } } class SOMain extends StatelessWidget { @override Widget build(BuildContext context) { return DefaultScaffold( "Main Screen", Center( child: RaisedButton( child: Text("Go to second screen"), onPressed: () { Navigator.pushNamed(context, '/secondPage'); }), ), ); } } class SOSecond extends StatelessWidget { @override Widget build(BuildContext context) { return Center( child: RaisedButton( child: Text("Go the 3rd screen"), onPressed: () => Navigator.pushNamed(context, "/thirdPage"), ), ); } } class SOThird extends StatelessWidget { @override Widget build(BuildContext context) { return Center(child: Text("You are on last screen")); } } 

Note: This is a simple workaround and may not be the best way to do this.

Comments

0

You can accomplish updating the state of the parent from a child by using a callback function.

Parent Class:

import 'package:flutter/material.dart'; class Parent extends StatefulWidget { @override State<StatefulWidget> createState() { return ParentState(); } } class ParentState extends State<Parent> { String title = "Old Title"; @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text(title), ), body: DaysFragmentView(onTitleSelect: (String value) { setTitle(value); } ), ); } void setTitle(String value) { setState(() { title = value; }); } } 

Child Class

 typedef TitleCallback = void Function(Title color); class DaysFragmentView extends StatelessWidget { const DaysFragmentView({this.onTitleSelect}); final TitleCallback onTitleSelect; @override Widget build(BuildContext context) { return Row( children: <Widget>[ RaisedButton( child: Text('One'), onPressed: () { onTitleSelect("TITLE ONE"); }, ), RaisedButton( child: Text('Two'), onPressed: () { onTitleSelect("TITLE TWO"); }, ) ], ); } } 

Reference:

Comments

0

Using ValueListenableBuilder is an option.

Use an instance variable

String appTitle; 

Then set the app bar as in the following block:

appBar: AppBar( ValueListenableBuilder<String>( valueListenable: appTitle, builder: (context, value, child) { return Text(appTitle.value); }, ), 

After that you can simply set appTitle.value in the other class. The title will be changed too because it listens to that value.

appTitle.value = "Home Screen"; 

Comments

0

Some answer here are too complicated. Here is a full working example using app bar update from child with scafold widget.

You can run the example in dart pad

import 'package:flutter/material.dart'; void main() { runApp(const MyHomePage(title: 'init title')); } class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { final ValueNotifier<String?> _appBarTitleNotifier = ValueNotifier<String?>(null); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: ValueListenableBuilder<String?>( builder: (BuildContext context, String? value, Widget? child) { return Text(value ?? widget.title); }, valueListenable: _appBarTitleNotifier, ), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ ChildDemoTitleBar(titleNotifier: _appBarTitleNotifier) ], ), ), ), ); } } class ChildDemoTitleBar extends StatefulWidget { final ValueNotifier<String?> titleNotifier; const ChildDemoTitleBar({Key? key, required this.titleNotifier}) : super(key: key); @override State<ChildDemoTitleBar> createState() => _ChildDemoTitleBarState(); } class _ChildDemoTitleBarState extends State<ChildDemoTitleBar> { int _counter = 0; @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.fromLTRB(20, 0, 20, 20), child: InkWell( onTap: () { _counter++; widget.titleNotifier.value = "title updated $_counter"; }, child: const Text("tap to update title"))); } } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.