Reputation: 343
I started Flutter recently and my app required bottom navigation. I have created bottom navigation and manage to access the child widget based on the tab selected.
Under the child widget there is drop down selection where I can change the bottom navigation text in one of the tabs for different selections.
I have tried a few days but still could not figure out how the child widget can change the text.
I have tried callback but cannot get it work. I have tried navigation.push - material page route but it rebuild the whole widget and my selection gone. I have also tried to use GlobalKey or Sharedpreference to capture my selection so that when it rebuild, it will use back the stored selection but I couldn't get it work.
I only wish to change the bottom navigation text in one of the text from child widget drop down selection.
Which is the best method to achieve this?
Upvotes: 0
Views: 1137
Reputation: 1649
I would recommend you try to use the bloc pattern with a StreamBuilder. I have an example below. Regardless, in the example there is a stateful widget, a bloc, and a data class. Try to understand this code and modify it to your needs.
import 'package:flutter/material.dart';
import 'dart:async';
class StreamScaffold extends StatefulWidget {
@override
_StreamScaffoldState createState() => _StreamScaffoldState();
}
class _StreamScaffoldState extends State<StreamScaffold> {
ScaffoldDataBloc bloc;
@override
void initState() {
super.initState();
bloc = ScaffoldDataBloc();
}
@override
Widget build(BuildContext context) {
return StreamBuilder<ScaffoldDataState>(
stream: bloc.stream, // The stream we want to listen to.
initialData: bloc.initial(), // The initial data the stream provides.
builder: (context, snapshot) {
ScaffoldDataState state = snapshot.data;
Widget page;
if (state.index == 0) {
// TODO separate this into its own widget, this is messy.
page = Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
onPressed: () => bloc.updateText(state,"Sales"),
child: Text("Set text to Sales")
),
RaisedButton(
onPressed: () => bloc.updateText(state, "Purchases"),
child: Text("Set text to Purchases"),
)
]),
);
}
if (state.index == 1) {
// TODO separate this into its own widget, this is messy.
page = Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
onPressed: () => bloc.updateText(state, "Stock"),
child: Text("Set text to Stock"),
),
RaisedButton(
onPressed: () => bloc.updateText(state, "Budget"),
child: Text("Set text to Budget"),
)
]));
}
return Scaffold(
body: page,
bottomNavigationBar: BottomNavigationBar(
currentIndex: state.index,
onTap: (int) => bloc.updateIndex(state, int),
items: [
BottomNavigationBarItem(
icon: Icon(Icons.play_arrow),
// Obtain the text from the state
title: Text(state.variableText)),
BottomNavigationBarItem(
icon: Icon(Icons.play_arrow), title: Text("Test")),
]),
);
});
}
@override
void dispose() {
super.dispose();
bloc.dispose();
}
}
// A data class to hold the required data.
class ScaffoldDataState {
int index;
String variableText;
ScaffoldDataState({this.index = 0, this.variableText = "Hello"});
}
// A bloc to handle updates of the state.
class ScaffoldDataBloc {
StreamController<ScaffoldDataState> scaffoldDataStateController = StreamController<ScaffoldDataState>();
Sink get updateScaffoldDataState => scaffoldDataStateController.sink;
Stream<ScaffoldDataState> get stream => scaffoldDataStateController.stream;
ScaffoldDataBloc();
ScaffoldDataState initial() {
return ScaffoldDataState();
}
void dispose() {
scaffoldDataStateController.close();
}
// Needs to be called every time a change should happen in the UI
// Add updated states into the Sink to get the Stream to update.
void _update(ScaffoldDataState state) {
updateScaffoldDataState.add(state);
}
// Specific methods for updating the different fields in the state object
void updateText(ScaffoldDataState state, String text) {
state.variableText = text;
_update(state);
}
void updateIndex(ScaffoldDataState state, int index) {
state.index = index;
_update(state);
}
}
Hope it helps!
Additional Questions from comment:
The easiest solution would be to simply pass the bloc as a parameter to the widget. Create a new dart file in your project, create a StatelessWidget there, create the code for the page in the build method. Note: it would make sense for you to separate the bloc into its own file along with the data class.
import 'package:flutter/material.dart';
// Import the file where the bloc and data class is located
// You have to have a similar import in the parent widget.
// Your dart files should be located in the lib folder, hit ctrl+space for
// suggestions while writing an import, or alt+enter on a unimported class.
import 'package:playground/scaffold_in_stream_builder.dart';
class ChildPage extends StatelessWidget {
final ScaffoldDataBloc bloc;
final ScaffoldDataState state;
const ChildPage({Key key, this.bloc, this.state}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(); // TODO replace with your page
}
}
However, if the these child widgets get their own children in separate files it would be better to use a InheritedWidget instead, with the bloc and state. This avoids "passing state down". See this article on inherited widgets
Upvotes: 0