I have created a provider to pass down the values within the WidgetTree, but when I try to retrieve the value, it gives me null as its value.
Initializing the value for the Provider:
GetCurrentCourse course = GetCurrentCourse();
course.currentCourse(
courseID: courseID,
courseDocID: courseDocID,
);
Below is the code related to the Provider:
import 'package:flutter/material.dart';
class GetCurrentCourse extends ChangeNotifier {
String? currentCourseID;
String? currentCourseDocID;
void currentCourse({courseID, courseDocID}) {
this.currentCourseDocID = courseDocID;
this.currentCourseID = courseID;
print("Current Course ID is ${this.currentCourseID}");
print("Current Course Doc ID is ${this.currentCourseDocID}");
notifyListeners();
}
}
When I print the values within the above GetCurrentCourse class. I do see the correct values getting printed.
Below is the code showcasing the Provider defined within the main.dart file as shown below:
ChangeNotifierProvider<GetCurrentCourse>(
create: (_) => GetCurrentCourse(),
),
Below is the code where I'm trying to consume the GetCurrentCourse Provider:
class CoursePageBody extends StatelessWidget {
#override
Widget build(BuildContext context) {
print("Printing Value of Course from Provider");
print(Provider.of<GetCurrentCourse>(context).currentCourseDocID);
.
.
}
}
Now, when I try to access the value of the Provider and try to print it. It prints null.
What can I try to fix it?
Your initialization code should be:
var courseProvider = Provider.of<GetCurrentCourse>(context, listen: false);
courseProvider.currentCourse(
courseID: courseID,
courseDocID: courseDocID,
);
And the widget that is going to access it should be wrapped in a Consumer that would look something like:
class CoursePageBody extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<GetCurrentCourse>(
builder: (context, courseProvider, child) {
print("Printing Value of Course from Provider");
print(courseProvider.currentCourseDocID);
// rest of the code
}
);
}
}
Related
I am currently trying to create an instance of a widget's state (ChangeNotifier) using a global auto-disposable ChangeNotifierProvider. The notifier instance takes in a few arguments to initialize each time the UI is built from scratch.
Let's assume we have the following simple state (or notifier):
class SomeState extends ChangeNotifier {
int _someValue;
SomeState({required int initialValue})
: _someValue = initialValue;
int get someValue => _someValue;
set someValue(int someValue) {
_someValue = someValue;
notifyListeners();
}
}
I used to use the Provider package before switching to Riverpod, where this could've easily been done like so:
class SomeWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
// Passing 2 into state initializer, which may be
// obtained from a different state, but not necessarily.
create: (_) => SomeState(initialValue: 2),
builder: (context, child) => Consumer<SomeState>(
builder: (context, state, child) {
// Will print 2, as it's currently the default value.
return Text('${state.someValue}');
},
),
);
}
}
So with Provider, you can manually call to SomeState constructor with arbitrary arguments when the state is being set up (i.e. provided). However, with Riverpod, it doesn't seem as intuitive to me, mainly because the provider is made to be declared globally:
static final someProvider = ChangeNotifierProvider.autoDispose((ref) => SomeState(2));
Which would end up being used like so:
class SomeWidget extends ConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
final state = ref.watch(someProvider);
return Text('${state.someValue}');
}
}
However, with this approach I can't pass parameters like I did in the example using Provider. I also don't want to use the family modifier because I would need to pass the same parameter each time I read/watch the state, even if it's already created.
If it helps, in my current situation I am trying to pass a function (say String Function()? func) into my state on initialization. It's also not feasible to depend on a different provider in this case which would provide such function.
How could I replicate the same functionality in the Provider example, but with Riverpod?
P.S. Apologies if code has syntax errors, as I hand-typed this and don't have an editor with me at the moment. Also, this is my first post so apologies for lack of clarity or format.
Use provider overrides with the param that you need:
First, let's ensure the ProviderScope in the root of the widget-tree.
// Root
ProviderScope(
child: MaterialApp(...)
)
After, create another one in some widget:
Widget build(BuildContext context) {
return ProviderScope(
overrides: [
someProvider.overrideWithProvider(
ChangeNotifierProvider.autoDispose((ref) => SomeState(5)),
),
],
child: Consumer(
builder: (context, ref, child) {
final notifier = ref.watch(someProvider);
final value = notifier.someValue;
return Text('$value'); // shows 5 instead of 2
}
),
);
}
If you do not want to use family then you can put value in another way by combining two providers.
final someValue = StateProvider((ref) => 0);
final someProvider = ChangeNotifierProvider.autoDispose((ref) {
final value = ref.watch(someValue);
return SomeState(value);
});
class SomeState extends ChangeNotifier {
int _someValue;
SomeState(int initialValue) : _someValue = initialValue;
int get someValue => _someValue;
set someValue(int someValue) {
_someValue = someValue;
notifyListeners();
}
}
USAGE:
// From everywhere you can put new value to your ChangeNotifier.
ref.read(someValue.notifier).state++;
But in your case, it's better to use the `family method. It's cleaner and less complicated.
I'm trying to change the variable from another stateful class.
class first extends statefulwidget {
bool text = false;
Widget build(BuildContext context) {
setState((){});
return Container(
child: text ? Text('Hello') : Text('check')
);
}
}
class second extends statefulwidget {
Widget build(BuildContext context) {
return Container(
child: IconButton(
onPressed: () {
first fir = first();
setState((){
fir.test = true;
});
}
)
);
}
}
widget shows only check not showing Hello
This is my code...Ignore spelling mistakes and camelcase
Give me the solutions if you know..
If you are trying to access data on multiple screens, the Provider package could help you. It stores global data accessible from all classes, without the need of creating constructors. It's good for big apps.
Here are some steps to use it (there is also a lot of info online):
Import provider in pubspec.yaml
Create your provider.dart file. For example:
class HeroInfo with ChangeNotifier{
String _hero = 'Ironman'
get hero {
return _hero;
}
set hero (String heroName) {
_hero = heroName;
notifyListeners();
}
}
Wrap your MaterialApp (probably on main.dart) with ChangeNotifierProvider.
return ChangeNotifierProvider(
builder: (context) => HeroInfo(),
child: MaterialApp(...),
);
Use it on your application! Call the provider inside any build method and get data:
#override
Widget build(BuildContext context){
final heroProvider = Provider.of<HeroInfo>(context);
return Column {
children: [
Text(heroProvider.hero)
]
}
}
Or set data:
heroProvider.hero = 'Superman';
try to reference to this answer, create function to set boolean in class1 and pass as parameter to class 2 and execute it :
typedef void MyCallback(int foo);
class MyClass {
void doSomething(int i){
}
MyOtherClass myOtherClass = new MyOtherClass(doSomething);
}
class MyOtherClass {
final MyCallback callback;
MyOtherClass(this.callback);
}
I want to use fetchdata() in another provider method and initialise variables.
you can use MultiProvider with ChangeNotifierProxyProvider
Action Class ( FirstModel )
class FirstModel with ChangeNotifier {
List<Strings> _names = ["Sat", "Sat2", "Sat3"];
List<Strings> get names {
return _names ;
}
}
Action Class ( SecondModel )
class SecondModel with ChangeNotifier {
SecondModel(this.firstModel);
final FirstModel firstModel;
List<Strings> getNames(){
return firstModel.names;
}
}
In main.dart just update the Multiprovider, example below
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider<FirstModel>(create: (_) => FirstModel()),
ChangeNotifierProxyProvider0<SecondModel>(
create: (BuildContext context) =>
SecondModel(Provider.of<FirstModel>(context, listen: false)),
update: (BuildContext context, SecondModel secondModel) =>
SecondModel(Provider.of<FirstModel>(context, listen: false)),
),
],
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
Similar other class also avilable...
For More Information please refer below link...
ChangeNotifierProxyProvider0 class API
If someone still has difficulties with this then maybe this will help:
Accessing one provider method into another provider:-
class 1:-
class OneState extends ChangeNotifier {
List<Strings> _names = ["Alex", "Brad", "Carol"];
List<GlobalKey> get names=> _names;
updateNames(
{required List<Strings> names}) {
_names = names;
notifyListners();
}
}
class 2:-
class AnotherState extends ChangeNotifier {
AnotherState({required this.watchOneStateProviderValue}); //<- Notice this.
OneState watchOneStateProviderValue; //<- Notice this.
late List<int> _iq;
List<GlobalKey> get iq=> _iq;
createIQFromNames() {
// Getting names from another provider.
List<String> names = watchOneStateProviderValue.names; //<- Notice this.
_iq = [];
for(var name in names){
if(name == "Park Ju-hyun"){
iq.add(99999);
} else {
iq.add(0);
}
}
notifyListners();
}
}
Now declare providers like this:-
final oneStateProvider =
ChangeNotifierProvider((ref) => OneState());
final anotherStateProvider = ChangeNotifierProvider((ref) {
// We use `ref.watch` to watch another provider, and we pass it the provider
// that we want to consume. Here: oneStateProvider
final CalendarPropertiesState watchCalendarPropertiesStateProviderValue =
ref.watch(calendarPropertiesStateProvider);
// We can then use the result to do something based on the value of `oneStateProvider`.
return AnotherState(watchOneStateProviderValue: watchOneStateProviderValue);
});
var prevProvider = Provider.of<PrevProvider>(context, listen: false);
await prevProvider.fetchdata();
Hope it will work
I have a class (that extends ChangeNotifier - Provider package) that has a function that returns a Future. What I'm trying to do is have it so that multiple futureBuilders in my UI code can receive values from this function but without having to call that function once per FutureBuilder.
However, I the function itself gets run again and again with every FutureBuilder I use. I know there must be a way to expose the Future itself through the Provider package but I can't seem to figure out how.
Here is the class that extends ChangeNotifier and has the future in it:
class ApiService extends ChangeNotifier {
CurrencyTicker _data;
CurrencyTicker get getdata => _data;
set setdata(CurrencyTicker data) {
_data = data;
}
Future<CurrencyTicker> fetchBaseData() async {
final response =
await http.get(API_URL_HERE); // url removed for stackoverflow
if (response.statusCode == 200) {
print('1 call logged');
setdata = CurrencyTicker.fromJson(json.decode(response.body));
return CurrencyTicker.fromJson(json.decode(response.body));
} else {
throw Exception('Request failed: ' + response.statusCode.toString());
}
}
}
Here is the UI code (it's just a FutureBuilder):
class MyBody extends StatelessWidget {
const MyBody({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
final provider = Provider.of<ApiService>(context);
return Center(
child: FutureBuilder(
future: provider.fetchBaseData(),
initialData: CurrencyTicker().price,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(snapshot.data.company),
],
);
} else {
return LinearProgressIndicator();
}
},
),
);
}
}
I haven't included the MultiProvider thats at the "top level" in the widget tree since I don't see why I'd have to. I haven't included the Model for the CurrencyTicker class. I can provide both of these if necessary.
Would appreciate any input at all here
You don't want to do the HTTP call directly inside the build method of your consumers.
Instead of exposing a method on your ChangeNotifier, you should expose a property:
class MyNotifier with ChangeNotifier {
Future<Foo> foo;
}
That foo is a variable that stores the result of your last fetchData call.
Depending on your needs, you can then:
call fetchData in the constructor, if immediately needed
class MyNotifier with ChangeNotifier {
MyNotifier() {
foo = fetchData();
}
Future<Foo> foo;
}
lazy load it using a custom getter:
class MyNotifier with ChangeNotifier {
Future<Foo> _foo;
Future<Foo> get foo => _foo ??= fetchData();
}
in flutter I use a class to load values for switch widgets from a database and then update that database when the switch is toggled. Somehow I need to have that class call setstate on the calling function of the instance but it doesn't seem to work.
See the code below for an example.
The first switch is how I'd write it without the database class. That is working fine, when tapping the switch it both moves and the print shows that the value changed.
In the second switch widget however, I used the database class to build it but it doesn't seem to call the callback function correctly. The print always prints false.
I thought I tried all combinations of => and (){} but something is still amiss. I'm pretty sure the problem is how the callback is handled in the line: callBackFunctionForSetState();
maybe that should be called with callBackFunctionForSetState((){}); but that also doesn't work.
import 'package:flutter/material.dart';
void main() {
runApp(App());
}
bool myBool = true;
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Title',
home: ScreenUpgrades(),
);
}
}
class ScreenUpgrades extends StatefulWidget {
#override
_ScreenUpgradesState createState() => _ScreenUpgradesState();
}
class _ScreenUpgradesState extends State<ScreenUpgrades> {
#override
Widget build(BuildContext ctxt) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Upgrades"),
),
body: FutureBuilder(
future: buildSwitchList(),
builder: (BuildContext ctxt, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView(children: snapshot.data);
} else {
return Center(child: CircularProgressIndicator());
}
}));
}
Future<List> buildSwitchList() async {
List<Widget> widgetList = [];
//This one below for a working example only
widgetList.add(Switch(value: myBool,onChanged: (bb)=>nonDBSetState()));
//Normally I'll create a bunch of widgets by loading their data from the DB as below
widgetList.add(DataBaseSwitchBuilder(1,()=>setState((){})).listViewWidget);
return widgetList;
}
nonDBSetState()
{
myBool = !myBool;
print('New value of first switch: ' + myBool.toString());
setState((){});
}
}
class DataBaseSwitchBuilder {
Widget listViewWidget;
int dbID;
bool onOff;
Function callBackFunctionForSetState;
DataBaseSwitchBuilder (int paramID, Function callBack)
{
dbID=paramID; //used to query the parameter from the DB
onOff = true;
callBackFunctionForSetState=callBack;
listViewWidget=(Switch(value: onOff,onChanged: (bb)=> updateDBAndState()));
}
updateDBAndState()
{
//update the switch
onOff = !onOff;
print('DB Swtich value now: ' + onOff.toString());
//first we save the record in the DB
//todo: code for updating DB
//Then call the passed function which should be a setstate from the calling function
//Below doesn't seem to work.
callBackFunctionForSetState();
}
}
I'm just expecting that the updateDBAndState will allow me to save the new value of the switch to the database and then call the setstate callback.
Just to respond to "How to pass setstate as a parameter to a class method"
widget controler
class Controller {
Controller._privateConstructor();
factory Controller() => _singleton;
static final Controller _singleton =
Controller._privateConstructor();
late Function setStateHandler;
void initSetState(Function setState) => setStateHandler = setState;
void triggerSetState() => setStateHandler();
}
widget
#override
void initState() {
super.initState();
controller.initSetState(() => setState(() {
widgetVariable = true;
}));
}