I'm new in Flutter and bloc architecture and I try some things.
Here I'll just try to set my background color, and it works with this code
final ColorBloc _bloc = ColorBloc();
#override
Widget build(BuildContext context) {
return StreamBuilder<Response<ColorResponse>>(
stream: _bloc.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
switch (snapshot.data.status) {
case Status.LOADING:
return Loading(loadingMessage: snapshot.data.message);
break;
case Status.COMPLETED:
return Scaffold(
appBar: AppBar(
title: Text('First Route clicked'),
),
backgroundColor: snapshot.data.data.color,
body: new Center(
child: new InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Act2()),
);
}, // Handle your callback
child: Ink(height: 100, width: 100, color: Colors.blue),
)),
floatingActionButton: FloatingActionButton(
onPressed: () {
_bloc.changeColor(Colors.yellow);
},
child: Icon(Icons.navigation),
backgroundColor: Colors.green,
));
break;
case Status.ERROR:
return Error(
errorMessage: snapshot.data.message,
//onRetryPressed: () => _bloc.fetchCategories(),
);
break;
}
}
);
}
}
But I need to rebuild all my page, can I just set the background color with the streamBuilder answer ?
EDIT: Should I use session to store my final color ?
Related
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Category',
),
),
body: Center(
child: FutureBuilder(
future: fetchCategory(),
builder: (ctx, snapShot) {
if (snapShot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else {
return ListView.builder(
itemCount: snapShot.data["table"].length,
itemBuilder: (context, index) {
return ListTile(
title: TextButton(
style: TextButton.styleFrom(
textStyle: TextStyle(fontSize: 20),
),
onPressed: () {
Navigator.pushNamed(context, '/second');
},
child: Text(snapShot.data["table"][index]["name"]),
),
//subtitle: Text("price: ${snapShot.data["table"][index]["price"]}"),
);
},
);
}
},
),
),
);
}
I want to transfer this data (snapShot.data["table"][index]["id"]) and use it to display the results on a second screen / data return String /
I want to use the data to display the items that have the same number on the second page, how can I do that
While you are using pushNamed(context, '/second'); and if receiver widget, don't have Constructor to get data, you can pass through ModalRoute like
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => WidgetA(),
settings: RouteSettings(
arguments: "id",
)),
);
and received on second screen as
class WidgetA extends StatelessWidget {
static final routeName = "/widgetA";
#override
Widget build(BuildContext context) {
final data = ModalRoute.of(context)!.settings;
late String retriveString;
if (data.arguments == null)
retriveString = "empty";
else
retriveString = data.arguments as String;
return Scaffold(
body: Column(
children: [
Text("Widget A"),
Text("Got data from parent $retriveString"),
],
),
);
}
}
I will suggest you to visit this where I've described different ways passing data.
you can pass data using argument
onPressed: () {
Navigator.pushNamed(context, '/second', arguments: snapShot.data["table"][index]);
},
I'm new in Flutter and I implemented the bloc architecture with streambuilder.
I created 2 pages with just a button which change my background color. All of theses pages are listening a stream to change the background color but when I change on the first page, it doesn't on the second.
But I want all my application change if 1 page decide to change it
Do I need to initialize a singleton bloc that my 2 screens used it ? Because for the moment each screen initializes its own bloc
Here is an example of 1 page (the second one is the same)
class Test extends StatelessWidget {
final ColorBloc _bloc = ColorBloc();
#override
Widget build(BuildContext context) {
return StreamBuilder<Response<ColorResponse>>(
stream: _bloc.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Scaffold(
appBar: AppBar(
title: Text('First Route clicked'),
),
backgroundColor: snapshot.data.data.color,
body: new Center(
child: new InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Act2()),
);
}, // Handle your callback
child: Ink(height: 100, width: 100, color: Colors.blue),
)),
floatingActionButton: FloatingActionButton(
onPressed: () {
_bloc.changeColor(Colors.yellow);
},
child: Icon(Icons.navigation),
backgroundColor: Colors.green,
));
}
return Scaffold(
appBar: AppBar(
title: Text('First Route'),
),
body: Center(
child: new InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Act2()),
);
}, // Handle your callback
child: Ink(height: 200, width: 200, color: Colors.red))),
floatingActionButton: FloatingActionButton(
onPressed: () {
_bloc.changeColor(Colors.yellow);
},
child: Icon(Icons.navigation),
backgroundColor: Colors.green,
));
},
);
}
}
To change the state of all screen when a bloc fires an event, you can use multiple StreamBuilder, but all of them need to listen to the bloc that fire the event. You can try these 2 ways:
Passing the bloc as parameter into the 2nd screen
class Test extends StatelessWidget {
final ColorBloc _bloc = ColorBloc();
#override
Widget build(BuildContext context) {
return StreamBuilder<Response<ColorResponse>>(
// ... other lines
body: new Center(
child: new InkWell(
onTap: () {
// Pass your bloc to the 2nd screen
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Act2(bloc: _bloc)),
);
},
// ... other lines
Use package such as provider package to pass the bloc down the tree. In your first screen, you can do this:
class Test extends StatelessWidget {
final ColorBloc _bloc = ColorBloc();
#override
Widget build(BuildContext context) {
// Use Provider to provide the bloc down the widget tree
return Provider(
create: (_) => _bloc,
child: StreamBuilder<Response<ColorResponse>>(
// ... other lines
Then in the 2nd screen (which I assume is Act2()), you get the ColorBloc from the Provider:
class Act2 extends StatefulWidget {
#override
_Act2State createState() => _Act2State();
}
class _Act2State extends State<Act2> {
ColorBloc _colorBloc;
#override
void didChangeDependencies() {
// Get the bloc in the 1st page
_colorBloc = Provider.of<ColorBloc>(context);
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
return StreamBuilder<Response<ColorResponse>>(
// Use the bloc like in the 1st page
stream: _colorBloc.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
// ... other lines
Small note: When using StreamBuilder you could initiate the value without the need to duplicate codes. Since I don't know the structure of your Response object, I'm taking Response(ColorResponse(color: Colors.green)) as the example:
// ... other lines
#override
Widget build(BuildContext context) {
return Provider(
create: (_) => _bloc,
child: StreamBuilder<Response<ColorResponse>>(
// Initiate your data here
initialData: Response(ColorResponse(color: Colors.green)),
stream: _bloc.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Scaffold(
appBar: AppBar(
title: Text('First Route clicked'),
),
backgroundColor: snapshot.data.data.color,
// ... other lines
}
// Don't need to copy the above code block for the case when the data is not streamed yet
return Container(child: Center(child: CircularProgressIndicator()));
},
),
);
}
I am new to Provider. I am trying to use the provider model across multiple pages.
I have 2 sequence of pages. In the first sequence, there is only one page IncrementerPage, which will just increment the count. In the second sequence, there is an OptionsPage, which will display the count value and has 2 option buttons(Increment & Decrement). By clicking these buttons will navigate to corrensponding pages IncrementerPage & DecrementerPage to increment & decrement the count.
Note: In sequence1 and sequence2, the count should always start from zero.
This is what I done.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home Page"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ChangeNotifierProvider<CounterModel>(
create: (context) => CounterModel(),
child: RaisedButton(
child: Text("Sequence1"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => IncrementerPage(),
),
);
},
),
),
ChangeNotifierProvider<CounterModel>(
create: (context) => CounterModel(),
child: RaisedButton(
child: Text("Sequence2"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => OptionsPage(),
),
);
},
),
),
],
),
),
);
}
}
class OptionsPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Consumer<CounterModel>(
builder: (context, model, child) {
return Text("Count: ${model.count}");
},
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text("Increment"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => IncrementerPage(),
),
);
},
),
RaisedButton(
child: Text("Decrement"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DecrementerPage(),
),
);
},
),
],
),
),
);
}
}
class IncrementerPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Incrementor"),
),
body: Center(
child: Consumer<CounterModel>(
builder: (context, model, child) {
return Text(
'${model.count}',
style: const TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
color: Colors.black54,
),
);
},
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
Provider.of<CounterModel>(context, listen: false).increment();
},
),
);
}
}
class DecrementerPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Decrementer"),
),
body: Center(
child: Consumer<CounterModel>(
builder: (context, model, child) {
return Text(
'${model.count}',
style: const TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
color: Colors.black54,
),
);
},
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.remove),
onPressed: () {
Provider.of<CounterModel>(context, listen: false).decrement();
},
),
);
}
}
class CounterModel extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
void decrement() {
_count--;
notifyListeners();
}
}
But I am getting this error:
Could not find the correct Provider<CounterModel> above this Consumer<CounterModel> Widget
By moving ChangeNotifierProvider<CounterModel> outside the MaterialApp fixed the error. But both sequence of pages seems to be using the same counter variable (or model like global). So how to pass different model object per each sequence of pages? or is there any better way to do this?.
Probably the following code is causing the exception:
floatingActionButton: FloatingActionButton(
child: Icon(Icons.remove),
onPressed: () {
Provider.of<CounterModel>(context, listen: false).decrement();
},
),
As you can see, the ChangeNotifierProvider for CounterModel is Wrapped around widgets RaisedButton() for sequence 1 and 2 only.
Which are under the body parameter. While the floatingActionButton a colleague parameter for body.
Meaning the Provider.of<CounterModel> can not find the Provider from it's parent widgets.
What you can do is, Move the ChangeNotifierProvider code to cover HomePage and remove every other ChangeNotifierProvider code for CounterModel in the app.
I was following the tutorial from the Flutter docs where you create a Startup naming app. The app consists in two pages: one where there's an infinite list of randomly generated startup names that you can add to your favorites, and a favorites page where you can see the names you saved.
After completing the tutorial, I tried to add some functionality of my own, I wanted to be able to Unfavorite a name by tapping it on the "Favorites" page. Below is the code that pushes the Favorites page to the navigator:
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext context) {
final Iterable<ListTile> tiles = _saved.map(
(WordPair pair) {
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
// Code I added //
trailing: Icon(Icons.delete),
onTap: () {
setState(() {
_saved.remove(pair);
});
},
// End //
);
},
);
final List<Widget> divided = ListTile
.divideTiles(
context: context,
tiles: tiles,
)
.toList();
return Scaffold(
appBar: AppBar(
title: Text('Saved suggestions'),
),
body: ListView(children: divided),
);
},
),
);
}
But it didn't worked as it should: you can indeed unsave names by tapping them, but the changes will only be shown on the screen after you go back to the main page and then to the favorites page again (or in other words, when Builder is called?).
So how do I fix this? Do I need to create a Stateful widget for the favorites page? If yes, how do I pass the _saved set to my new widget?
If anybody needs the whole code:
https://pastebin.com/asLneaKe
Wrap with StatefulBuilder works fine.
You can see full code and working demo
code snippet
MaterialPageRoute<void>(
builder: (BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
final Iterable<ListTile> tiles = _saved.map(
working demo
full code
import 'package:english_words/english_words.dart' as prefix0;
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Startup Name Generator',
theme: ThemeData(
primaryColor: Colors.white,
),
home: RandomWords(),
);
}
}
class RandomWords extends StatefulWidget {
#override
RandomWordsState createState() => RandomWordsState();
}
class RandomWordsState extends State<RandomWords> {
final List<WordPair> _suggestions = <WordPair>[];
final Set<WordPair> _saved = Set<WordPair>();
final TextStyle _biggerFont = const TextStyle(fontSize: 18.0);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Startup Name Generator'), actions: <Widget>[
// Icone 3 linhas
IconButton(
icon: Icon(Icons.list),
onPressed: _pushSaved,
),
]),
body: _buildSuggestions(),
);
}
Widget _buildRow(WordPair pair) {
final bool alreadySaved = _saved.contains(pair);
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
trailing: Icon(
alreadySaved ? Icons.favorite : Icons.favorite_border,
color: alreadySaved ? Colors.red : null,
),
onTap: () {
setState(() {
if (alreadySaved) {
_saved.remove(pair);
} else {
_saved.add(pair);
}
});
});
}
Widget _buildSuggestions() {
return ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: (context, i) {
if (i.isOdd) return Divider();
final index = i ~/ 2;
if (index >= _suggestions.length) {
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
},
);
}
void _pushSaved() {
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
final Iterable<ListTile> tiles = _saved.map(
(WordPair pair) {
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
// Code I added //
trailing: Icon(Icons.delete),
onTap: () {
setState(() {
_saved.remove(pair);
});
},
// End //
);
},
);
final List<Widget> divided = ListTile.divideTiles(
context: context,
tiles: tiles,
).toList();
return Scaffold(
appBar: AppBar(
title: Text('Saved suggestions'),
),
body: ListView(children: divided),
);
});
},
),
);
}
}
I'm trying to create a screen in flutter/dart that has three horizontal lines at the top left corner of the screen and when clicked on it, it displays a list of clickable texts, when clicked on it, it has to take to the next page. Any help will be appreciated.
ListTile and ListView widgets are used to represent a tile as a list in flutter and Drawer widget is the horizontal lines things that you are talking about, i suppose.
Here is the sample code for it:
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget{
#override
_State createState() => new _State();
}
class _State extends State<HomePage> {
bool _value1 = false;
bool _value2 = false;
void _onChanged1(bool value) => setState(() => _value1 = value);
void _onChanged2(bool value) => setState(() => _value2 = value);
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
backgroundColor: Color(0xFF293b72),
title: new Text('Hughes InstaGrade'),
),
drawer: new Drawer(
child: new ListView(
children: <Widget> [
//new DrawerHeader(child: new Text(''),),
new ListTile(
title: new Text('Home'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HomePage(),
),
);
},
),
new ListTile(
title: new Text('New Dull Grade'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FirstForm(),
),
);
},
),
//new Divider(),
new ListTile(
title: new Text('Search'),
onTap: () {
},
),
//new Divider(),
new ListTile(
title: new Text('Filter'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FilterPage(),
),
);
},
),
new ListTile(
title: new Text('Forms'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Forms(title: "Forms"),
),
);
},
),
new SwitchListTile(
value: _value2,
onChanged: _onChanged2,
title: new Text('My Bits/All Bits', ),
),
new ListTile(
title: new Text('Log Out'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LoginPage(),
),
);
},
),
// new Switch(value: _value1, onChanged: _onChanged1),
],
)
),
body: DataScreen(),
);
}
}
String _formatDate(String date) {
DateTime d = DateTime.parse(date);
return DateFormat("dd-MM-yyyy").format(d);
}
}
class DataScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Data Screen"),
),
body: Text("Sample App"),),
);
}
}