send data from widget to another widget in flutter - flutter

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()

Related

how to call flutter function that is inside stateful widget in outside function?

I want to call the _setSilentMode() function inside fireAlarm().
_setSilentMode() is inside statefull widget but fireAlarm() is outside stateful widget. whenever i put _setSilentMode() function inside fireAlarm() it gives error.
class _MyAppState extends State<MyApp> {
Future<void> _setSilentMode() async {
RingerModeStatus status;
try {
status = await SoundMode.setSoundMode(RingerModeStatus.silent);
setState(() {
_soundMode = status;
});
} on PlatformException {
print('Do Not Disturb access permissions required!');
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: const Text('Welcome to Flutter'),
),
body: SingleChildScrollView(
child: Column(
children: [
Switch(
onChanged: (value) {
setState(() {
isOn = value;
});
AndroidAlarmManager.oneShot(
Duration(seconds: 15),
alarmId,
fireAlarm,
);
print('Alarm set at ${DateTime.now()}');
},
value: isOn,
activeColor: Colors.blue,
activeTrackColor: Colors.yellow,
inactiveThumbColor: Colors.redAccent,
inactiveTrackColor: Colors.orange,
)
],
))),
);
}
}
void fireAlarm() {
print('Alarm Fired at ${DateTime.now()}');
// i want to call _setSilentMode() here but it gives me error
_setSilentMode();
}
please help me that how to solve this problem and add _setSilentMode() inside fireAlarm() to work.
You can use GlobalKey to access the State and call method of state to update UI. You need to make state and method public.
Alternatively, I recommend using state management libraries such as RiverPod or Provider.
The method is outside the State class. For such you simply add widget before calling it
widget.fireAlarm

how to access variable value , from a different screen in flutter?

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.

Flutter send and show data between BottomNavigationBarItem

I want to share and show data between BottomNavigationBarItems
It is my BottomNavigationBar page:
class BottomNavigationBarController extends StatefulWidget {
#override
_BottomNavigationBarControllerState createState() =>
_BottomNavigationBarControllerState();
}
class _BottomNavigationBarControllerState
extends State<BottomNavigationBarController> {
List<Widget> pages = [];
#override
void initState() {
pages.add(HomePage(
key: PageStorageKey('HomePage'),
));
pages.add(MapPage(
key: PageStorageKey('MapPage'),
));
super.initState();
}
int _currentIndex = 0;
Widget _bottomNavigationBar(int selectedIndex) => BottomNavigationBar(
onTap: (int index) => setState(() => _currentIndex = index),
currentIndex: selectedIndex,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(icon: Icon(Icons.home), label: "Home"),
BottomNavigationBarItem(icon: Icon(Icons.map), label: "MapPage"),
],
);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("iArrived"),
actions: _currentIndex != 1
? <Widget>[
IconButton(
icon: Icon(Icons.keyboard_arrow_right),
onPressed: () {
setState(() {
_currentIndex++;
});
})
]
: null),
backgroundColor: Colors.white,
bottomNavigationBar: _bottomNavigationBar(_currentIndex),
body: IndexedStack(
index: _currentIndex,
children: pages,
),
);
}
}
And I want to send my location from the MapPage to the HomePage.
There is a function in the MapPage that is executed when the place change:
void _setLocation(LatLng latlng) {
setState(() {
lat = latlng.latitude;
lng = latlng.longitude;
});
}
I think I have to call some function like a callback inside _setLocation() but I don't know how to implement it to show the location on my HomePage and have it refresh every time I change it.
Thanks for the help.
If your answer is to use shared_preferences plugin, can you tell me how would you implement it? Because I tried it and it did not work for me.
Thanks again.
You can use provider plugin for state management:
Create a model that store a shared value:
class LocationModel extends ChangeNotifier {
Location location;
void updateLocation(Location location) {
this.location = location;
notifyListeners();
}
}
Wrap your root widget with Provider class:
#override
Widget build(BuildContext build) {
return ChangeNotifierProvider(
create: (_) => LocationModel(),
child: App(),
);
}
When you changing a shared value in a model (you need to get a model from Provider, it depends on concrete implementation). For example:
onTap() {
model.updateLocation(newLocation):
}

How to access a function from a different dart file in Flutter?

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.

How to implement a Slider within an AlertDialog in Flutter?

I am learning app development on Flutter and can't get my Slider to work within the AlertDialog. It won't change it's value.
I did search the problem and came across this post on StackOverFlow:
Flutter - Why slider doesn't update in AlertDialog?
I read it and have kind of understood it. The accepted answer says that:
The problem is, dialogs are not built inside build method. They are on a different widget tree. So when the dialog creator updates, the dialog won't.
However I am not able to understand how exactly does it have to be implemented as not enough background code is provided.
This is what my current implementation looks like:
double _fontSize = 1.0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(qt.title),
actions: <Widget>[
IconButton(
icon: Icon(Icons.format_size),
onPressed: () {
getFontSize(context);
},
),
],
),
body: ListView.builder(
padding: EdgeInsets.symmetric(vertical: 15.0),
itemCount: 3,
itemBuilder: (context, index) {
if (index == 0) {
return _getListTile(qt.scripture, qt.reading);
} else if (index == 1) {
return _getListTile('Reflection:', qt.reflection);
} else {
return _getListTile('Prayer:', qt.prayer);
}
})
);
}
void getFontSize(BuildContext context) {
showDialog(context: context,builder: (context){
return AlertDialog(
title: Text("Font Size"),
content: Slider(
value: _fontSize,
min: 0,
max: 100,
divisions: 5,
onChanged: (value){
setState(() {
_fontSize = value;
});
},
),
actions: <Widget>[
RaisedButton(
child: Text("Done"),
onPressed: (){},
)
],
);
});
}
Widget parseLargeText(String text) {...}
Widget _getListTile(String title, String subtitle) {...}
I understand that I will need to make use of async and await and Future. But I am not able to understand how exactly. I've spent more than an hour on this problem and can't any more. Please forgive me if this question is stupid and noobish. But trust me, I tried my best.
Here is a minimal runnable example. Key points:
The dialog is a stateful widget that stores the current value in its State. This is important because dialogs are technically separate "pages" on your app, inserted higher up in the hierarchy
Navigator.pop(...) to close the dialog and return the result
Usage of async/await
import 'package:flutter/material.dart';
void main() => runApp(App());
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
double _fontSize = 20.0;
void _showFontSizePickerDialog() async {
// <-- note the async keyword here
// this will contain the result from Navigator.pop(context, result)
final selectedFontSize = await showDialog<double>(
context: context,
builder: (context) => FontSizePickerDialog(initialFontSize: _fontSize),
);
// execution of this code continues when the dialog was closed (popped)
// note that the result can also be null, so check it
// (back button or pressed outside of the dialog)
if (selectedFontSize != null) {
setState(() {
_fontSize = selectedFontSize;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Font Size: ${_fontSize}'),
RaisedButton(
onPressed: _showFontSizePickerDialog,
child: Text('Select Font Size'),
)
],
),
),
);
}
}
// move the dialog into it's own stateful widget.
// It's completely independent from your page
// this is good practice
class FontSizePickerDialog extends StatefulWidget {
/// initial selection for the slider
final double initialFontSize;
const FontSizePickerDialog({Key key, this.initialFontSize}) : super(key: key);
#override
_FontSizePickerDialogState createState() => _FontSizePickerDialogState();
}
class _FontSizePickerDialogState extends State<FontSizePickerDialog> {
/// current selection of the slider
double _fontSize;
#override
void initState() {
super.initState();
_fontSize = widget.initialFontSize;
}
#override
Widget build(BuildContext context) {
return AlertDialog(
title: Text('Font Size'),
content: Container(
child: Slider(
value: _fontSize,
min: 10,
max: 100,
divisions: 9,
onChanged: (value) {
setState(() {
_fontSize = value;
});
},
),
),
actions: <Widget>[
FlatButton(
onPressed: () {
// Use the second argument of Navigator.pop(...) to pass
// back a result to the page that opened the dialog
Navigator.pop(context, _fontSize);
},
child: Text('DONE'),
)
],
);
}
}
You just need to warp the AlertDialog() with a StatefulBuilder()