Q1: how can i get the a certain variable value that is being used in a different screen?
i am trying to change the background color of appbar and button by reading a variable that is being used on another screen ?
Explanation:
I am fairly new to flutter and experimenting with different things, i have about 5 to six flutter screens and on the 5th screen there is a dropdown list which has 3 values see the code :
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: ListTile(title: Text("select one",style: TextStyle(fontfamily: "Ubuntu",
fontWeight: FontWeight.w200,fontSize: 18,color: Colors.black),),
subtitle: DropdownButton(
isExpanded: true,
items: [
DropdownMenuItem(child: Text("option1"),value: "option1",),
DropdownMenuItem(child: Text("option2"), value: "option2"),
DropdownMenuItem(child: Text("option3"), value: "option3"),
],
i want to get these values and use them on other screens based on the selected option
for example : if option 1 is selected then do this
if option 2 is selected then do that
Q2: i intend to use simple if statements , once i can get the values from the screen5 , is using if statement the right way to of doing it ?
EDIT 1: i did what Nitesh suggested :
this is my code in the 5th screen:
print(Value.getString());
but i am getting the error that error: The method 'getString' isn't defined for the type 'Value'.
I advise you to learn about State Management.
Here is a solution based on Riverpod and Flutter Hooks.
Define a Provider (Here, a StateProvider)
final optionProvider = StateProvider<String>((ref) => options.first);
Watch its state inside the build method
final _currentOption = useProvider(optionProvider).state;
Modify its state
onChanged: (value) => context.read(optionProvider).state = value,
Full source code:
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
void main() {
runApp(
ProviderScope(
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
home: HomePage(),
),
),
);
}
class HomePage extends HookWidget {
#override
Widget build(BuildContext context) {
final _currentOption = useProvider(optionProvider).state;
return Scaffold(
appBar: AppBar(
title: Text('MyApp'),
actions: [
IconButton(
icon: Icon(Icons.settings),
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SettingsPage(),
),
),
),
],
),
body: Center(child: Text('CURRENT: $_currentOption')),
);
}
}
class SettingsPage extends HookWidget {
#override
Widget build(BuildContext context) {
final _currentOption = useProvider(optionProvider).state;
return Scaffold(
appBar: AppBar(title: Text('Settings')),
body: Center(
child: DropdownButton(
isExpanded: true,
value: _currentOption,
items: options
.map((option) => DropdownMenuItem(
child: Text(option),
value: option,
))
.toList(),
onChanged: (value) => context.read(optionProvider).state = value,
),
),
);
}
}
final optionProvider = StateProvider<String>((ref) => options.first);
final options = List.generate(5, (index) => 'Option $index').toList();
Other State Management Systems
You have also other State Management packages available, as described here: https://flutter.dev/docs/development/data-and-backend/state-mgmt/options
You can do it by creating a new class that will store the value for you.
Something like this:
class Value {
static String value;
static void setString(String newValue) {
value = newValue;
}
static String getString() {
return value;
}
}
So, when your dropdown is clicked you can call the
Value.setString(value)
to store the value.
And on the 5th screen you can call the
Value.getString()
to get the stored value and use it.
Note: There are a lot of different ways to do it. But this is the simplest, according to me.
Related
I need to use RadioListTile in PopupMenuButton, but the default implementation of PopupMenuButton doesn't allow it because PopupMenuButton has its onSelected method and Radio class have its onChanged method so they are managing their own state if I use Radio class onChanged method then it doesn't dismiss/close PopupMenuButton which default behaviour when I use its onSelected method and it doesn't update radio buttons so for that I am using a workaround described here but with some modifications, I am using ValueListenableBuilder instead of AnimatedBuilder but the problem is that I want to its default behaviour of auto dismiss/closing when I select an item which is not from onSelected now.
here is the minimal sample
import 'package:flutter/material.dart';
void main() => runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
actions: [
SelectionPopupMenu(),
],
),
),
),
);
class SelectionPopupMenu extends StatelessWidget {
#override
Widget build(BuildContext context) {
final _selectionList = [
'First',
'Second',
];
final _selectedValueNotifier = ValueNotifier(0);
return PopupMenuButton<int>(
onSelected: ,
itemBuilder: (context) => List.generate(
_selectionList.length,
(index) => PopupMenuItem(
child: ValueListenableBuilder(
valueListenable: _selectedValueNotifier,
builder: (context, value, child) => RadioListTile<int>(
value: index,
title: Text(_selectionList[index]),
groupValue: _selectedValueNotifier.value,
onChanged: (value) {
_selectedValueNotifier.value = value!;
},
),
),
),
),
);
}
}
solution for this just use Navigator.pop(context); for any dialog, popup menu, or screen to go back or removed from screen i used it like this
onChanged: (value) {
selectedValueNotifier.value = value!;
Navigator.pop(context);
},
I create some tab functions dynamically. I need only 10 tabs in that dynamic function can you respond to the condition for this method.
Dynamic list of Tabs
Here is a small example app displaying a random dynamic list of Sports.
Domain Layer
Each TabItem is defined with a name and a description.
part '66457422.dynamic_tabs.freezed.dart';
#freezed
abstract class TabItem implements _$TabItem {
const factory TabItem({
String name,
String description,
}) = _TabItem;
const TabItem._();
String get initials =>
name.split(RegExp(r'\W+')).map((x) => x[0].toUpperCase()).join('');
static TabItem get random {
final faker = Faker();
return TabItem(
name: faker.sport.name(),
description: faker.lorem.sentence(),
);
}
}
I used the freezed package that provides immutability for my TabItem. freezed uses the build_runner package to generate the class implementation inside 66457422.dynamic_tabs.freezed.dart.
In my TabItem, I defined to getters:
String get initials to get the initials of each Sport to be used as the Tabs labels.
static TabItem get random to generate a random Sport thanks to the faker package.
State Management
To maintain the list of TabItems, you will need some kind of State Management. In this example, I use the riverpod package (in its flutter_hooks flavor) from the same author as Provider. But, you may use other State Management systems if you prefer. Check this curated list.
My tabsProvider is a StateNotifierProvider defined as follows:
final tabsProvider = StateNotifierProvider<TabsNotifier>(
(ref) => TabsNotifier()..addTab()..addTab()..addTab());
class TabsNotifier extends StateNotifier<List<TabItem>> {
TabsNotifier([List<TabItem> state]) : super(state ?? []);
void addTab() {
state = [
...state,
TabItem.random,
];
}
}
Presentation Layer
Now, we just need to display everything!
I used the DefaultTabController Widget and maps on the List<TabItem> provided by my tabsProvider to display the Tabs and the TabViews:
class HomePage extends HookWidget {
#override
Widget build(BuildContext context) {
final tabs = useProvider(tabsProvider.state);
return DefaultTabController(
length: tabs.length,
child: Scaffold(
appBar: AppBar(
title: Text('Dynamic Tabs Demo'),
bottom: TabBar(
tabs: tabs.map((t) => Tab(text: t.initials)).toList(),
),
),
body: TabBarView(
children: tabs.map((t) => TabItemWidget(tabItem: t)).toList(),
),
floatingActionButton: tabs.length >= 10
? null
: FloatingActionButton(
onPressed: () => context.read(tabsProvider).addTab(),
child: Icon(Icons.add),
),
),
);
}
}
The FloatingActionButton will addTab() on my tabsProvider only if the number of TabItems is smaller than 10. Otherwise, it disappears.
VoilĂ ! That's about it.
Full source code for easy copy-paste
import 'package:faker/faker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
part '66457422.dynamic_tabs.freezed.dart';
void main() {
runApp(
ProviderScope(
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Dynamic Tabs Demo',
home: HomePage(),
),
),
);
}
class HomePage extends HookWidget {
#override
Widget build(BuildContext context) {
final tabs = useProvider(tabsProvider.state);
return DefaultTabController(
length: tabs.length,
child: Scaffold(
appBar: AppBar(
title: Text('Dynamic Tabs Demo'),
bottom: TabBar(
tabs: tabs.map((t) => Tab(text: t.initials)).toList(),
),
),
body: TabBarView(
children: tabs.map((t) => TabItemWidget(tabItem: t)).toList(),
),
floatingActionButton: tabs.length >= 10
? null
: FloatingActionButton(
onPressed: () => context.read(tabsProvider).addTab(),
child: Icon(Icons.add),
),
),
);
}
}
class TabItemWidget extends StatelessWidget {
final TabItem tabItem;
const TabItemWidget({Key key, this.tabItem}) : super(key: key);
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(16.0),
child: Column(
children: [
Text(tabItem.name),
const SizedBox(height: 24.0),
Text(tabItem.description),
],
),
);
}
}
final tabsProvider = StateNotifierProvider<TabsNotifier>(
(ref) => TabsNotifier()..addTab()..addTab()..addTab());
class TabsNotifier extends StateNotifier<List<TabItem>> {
TabsNotifier([List<TabItem> state]) : super(state ?? []);
void addTab() {
state = [
...state,
TabItem.random,
];
}
}
#freezed
abstract class TabItem implements _$TabItem {
const factory TabItem({
String name,
String description,
}) = _TabItem;
const TabItem._();
String get initials =>
name.split(RegExp(r'\W+')).map((x) => x[0].toUpperCase()).join('');
static TabItem get random {
final faker = Faker();
return TabItem(
name: faker.sport.name(),
description: faker.lorem.sentence(),
);
}
}
This is my HomePage.dart Codes. Here I Want to access the _pushSaved() function into the 'onpressed' property from a different file named "randomwords.dart" file (code attached for review). It seems I need to declare the _pushSaved() somewhere but do not have any idea as I am new to dart and flutter. I Will be thankful if somebody could help. Thanks in advance.
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("StartUp Name Generator"),
actions: [
IconButton(icon: Icon(Icons.list), **onPressed: _pushSaved**)
],
),
body: RandomWords(),
);
}
}
The _pushSaved() function code in the randomwords.dart file needs to be accessed in the HomePage.dart as object...
class RandomWords extends StatefulWidget {
#override
_RandomWordsState createState() => _RandomWordsState();
}
class _RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _saved = Set<WordPair>();
final _biggerFont = TextStyle(fontSize: 20.0, color: Colors.pink);
#override
Widget build(BuildContext context) {
return ListView.builder(
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]);
},
);
}
Widget _buildRow(WordPair pair) {
final 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);
}
});
},
);
}
void **_pushSaved()** {
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext context) {
final tiles = _saved.map(
(WordPair pair) {
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
);
},
);
final divided =
ListTile.divideTiles(tiles: tiles, context: context).toList();
return Scaffold(
appBar: AppBar(
title: Text("Saved Suggestions"),
),
body: ListView(
children: divided,
),
);
},
),
);
}
}
Although the answer above solves your problem , but thats not a good practice , use absctract classes for defining functions that are supposed to be used at various places.
Your function is defined as a private property. You need to change that first.
pushSaved()
Secondly, You need to define this function as a static function to access it in other classes.
static void pushSaved()
Now call your function in your HomePage class in onPressed function like this:
onPressed: () => RandomWords.pushSaved
Import the HomePage.dart in the Homepage file like this;
import 'package:main.dart' as main; //it doesn't have to be as 'main'
and then acces the function like this:
IconButton(icon: Icon(Icons.list),*onPressed: main._pushSaved)
Note: now when I've done somthing like this and my function startes with a _ it unfortunantly doesn't work, I know that everyone makes functions with _ first, but apperantly it didn't work for me when importing it from another file. So beware of that.
Also the function has to be out of the _RandomWordsState to be accesed by the other file. Just put it down underneath.
I want to send data from widget to another widget, in my example i want to send some filter data from FilterScreen.dart to ShopScreen.dart
it works fine but i dont know is what i'm doing is correct?
in filter model file:
class FilterData with ChangeNotifier {
bool isFreeShipping;
bool isSomeThingElse;
FilterData({this.isFreeShipping = false, this.isSomeThingElse = false});
void setFreeShippingValue(bool newval) {
isFreeShipping = newval;
notifyListeners();
}
void setSomeThingElseValue(bool newval) {
isSomeThingElse = newval;
notifyListeners();
}
}
in main.dart:
return ChangeNotifierProvider(
create: (context) => FilterData(),
child: MaterialApp(
.........
)
);
in tabs screen:
class TabsScreen extends StatefulWidget {
#override
_TabsScreenState createState() => _TabsScreenState();
}
class _TabsScreenState extends State<TabsScreen> {
List<Map<String, Object>> _pages;
int _selectedPageIndex = 0;
#override
void initState() {
_pages = [
{
'page': ShopScreen(),
'title': 'shop',
},
{
'page': FilterScreen(),
'title': 'filter',
},
];
super.initState();
}
void _selectPage(int index) {
setState(() {
_selectedPageIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_pages[_selectedPageIndex]['title']),
),
drawer: DrawerApp(),
body: _pages[_selectedPageIndex]['page'],
bottomNavigationBar: BottomNavigationBar(
onTap: _selectPage,
backgroundColor: Theme.of(context).primaryColor,
unselectedItemColor: Colors.white,
selectedItemColor: Theme.of(context).accentColor,
currentIndex: _selectedPageIndex,
// type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
backgroundColor: Theme.of(context).primaryColor,
icon: Icon(Icons.shop),
title: Text('Shop'),
),
BottomNavigationBarItem(
backgroundColor: Theme.of(context).primaryColor,
icon: Icon(Icons.search),
title: Text('Filter'),
),
],
),
);
}
}
in FilterScreen.dart:
class FilterScreen extends StatefulWidget {
#override
_FilterScreenState createState() => _FilterScreenState();
}
class _FilterScreenState extends State<FilterScreen> {
#override
Widget build(BuildContext context) {
final data = Provider.of<FilterData>(context);
return Container(
child: Center(
child: Expanded(
child: ListView(
children: <Widget>[
SwitchListTile(
title: Text('Free Shipping'),
value: data.isFreeShipping,
subtitle: Text('get free shipping products'),
onChanged: (newValue) {
data.setFreeShippingValue(newValue);
}),
SwitchListTile(
title: Text('Some thing else'),
value: data.isSomeThingElse,
subtitle: Text('get filtred products'),
onChanged: (newValue) {
data.setSomeThingElseValue(newValue);
}),
],
),
),
),
);
}
}
in ShopScreen.dart:
class ShopScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
final data = Provider.of<FilterData>(context);
return Container(
child: Center(
child: Text(
data.isFreeShipping ? 'get favorite Products' : 'get all products'),
),
);
}
}
enter image description here
Your question indeed is a pain for most of the developers, which is like I don't know how it works!
So, if you are not able to understand. there are two reasons to that:
You just blindly followed the tutorial or documentation, cos of the time constraints
You did not understand how Flutter Provider State Management works. So, for that, do read upon these:
List of state managements in flutter
Flutter provider package, of course you have used that in your project. But read how he is using.
So, now let us jump to the code. How your code works?
There are multiple things which are responsible for this:
1. Provider Wrap: If you closely look into the main.dart code, you have done this
return ChangeNotifierProvider(
create: (context) => FilterData(), // here you define the ChangeNotifier class
child: MaterialApp(
.........
)
);
Now looking at the above code, you see, whenever you wrap the app with the ChangeNotifierProvider(), it always rebuilds whenever there is a state change in the class which you have provided inside that, in this case FilterData(). Any changes happens will reflect in the whole app, cos, ChangeNotifierProvider(), is keep rebuilding the state of the immediate child, in this case your, MaterialApp(), which is wrapped.
2. NotifyChanges from the ChangeNotifier class: If you look at your FilterData, it is the one which is responsible for the rebuilding of the app, which is wrapped by the ChangeNotifierProvider().
Let us see how:
void setFreeShippingValue(bool newval) {
isFreeShipping = newval;
notifyListeners();
}
void setSomeThingElseValue(bool newval) {
isSomeThingElse = newval;
notifyListeners();
}
If you closely take a look at the methods, which I mentioned in the above code from your FilterData class only, they have notifyListeners(). These are the ones, which is responsible, whenever your two methods called, it notifies the ChangeNotifierListener to rebuild the widget, and hence you see the updated data every time, you use any of the two methods
3. Using NotifyListeneres method from the FilterData in FilterScreen: So, again if we look closely at the thing which we have mentioned in the point 2, we see that, the method method should be called to make changes in the App which is the immediate child of ChangeNotifierProvider()
SwitchListTile(
title: Text('Free Shipping'),
value: data.isFreeShipping,
subtitle: Text('get free shipping products'),
onChanged: (newValue) {
data.setFreeShippingValue(newValue);
}),
SwitchListTile(
title: Text('Some thing else'),
value: data.isSomeThingElse,
subtitle: Text('get filtred products'),
onChanged: (newValue) {
data.setSomeThingElseValue(newValue);
}),
So, when you call any of the methods in your onChanged, it straight away notifies the Provider that, the value has been changed, and the app rebuilds, and when you switch to the other tab, you see updated result like magic.
MOST IMPORTANT: Your final data = Provider.of<FilterData>(context);, is an instance of the Provider class, which trigger the method to help notify the ChangeNotifierProvider() to make changes in the app
So the mapping is like that:
Listens to the change
FilterData {setFreeShippingValue, setSomeThingElseValue} <----------------------> ChangeNotifierProvider() REBUILDS MATERIALAPP()
I have this piece of code. It contains a list of countries with a favorite icon in front of each list tiles. I want the tapped text to save/unsave in Favorite_Page. I tried this many times but could not get what I really want to do. Please explain the whole code if possible.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("Favorite Item"),
),
body: FavoriteScreen(),
),
initialRoute: '/',
routes: {
'/': (ctx) => FavoriteItem(),
},
);
}
}
class FavoriteItem extends StatefulWidget {
#override
_FavoriteItemState createState() => _FavoriteItemState();
}
class _FavoriteItemState extends State<FavoriteItem> {
static final List<String> countryList = [
'India',
'America',
'Australia',
'Russia',
'Japan',
'China',
'Nepal',
];
List<bool> _isFavorited = List.filled(countryList.length, false);
#override
Widget build(BuildContext context) {
return Center(
child: ListView.builder(
itemCount: countryList.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(
countryList[index],
),
trailing: IconButton(
onPressed: () =>
setState(() => _isFavorited[index] = !_isFavorited[index]),
icon: _isFavorited[index]
? Icon(Icons.favorite)
: Icon(Icons.favorite_border),
),
);
},
),
);
}
}
(I'm French so I have probably made some mistakes in my answer : sorry)
This piece of code just initialize a list of country. The list _isFavorited is filled with false at the beginning. That's mean that by clicking on an element, you just set to true the element at the position of the country in _isFavorited. I will explain with an example, with the list of country ['Japan','USA",'France'], _isFavorited = [false,false,false]. If I click on USA, _isFavorited will be equal to [false,true,false]. Your piece of code is saving this but not the text as you requested..
You may try this :
List <String> _isFavorited=[];
...
setState(){
if (_isFavorited.contains(countryList[index])){
_isFavorited.remove(country[index]);
}else{
_isFavortied.add(country[index]);
}
}
As a result, you might have a list called _isFavorited filled with name of country you liked. You can print this list each time you tap on a country to check if it works! Is this what you were looking for ?
I hope it could help you !