I am using notifyListner from flutter Provider package but my UI is not updating - flutter

I am using notifyListner from flutter Provider package but my UI is not updating whenever I am typing the letters in the TextField. I am making this app just to understand how Provider works. My appbar and text is supposed to change whenever I type the text in TextFiled. Here's my code,
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<Data>(
create: (context) => Data(),
child: MaterialApp(
home: Scaffold(
appBar: AppBar(
title: MyAppBar(),
),
body: SafeArea(
child: Column(
children: [
MyTextField(),
Expanded(
child: MyTextWidget(),
),
],
),
),
),
),
);
}
}
class MyTextField extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: TextField(
onChanged: (newValue) {
Provider.of<Data>(context, listen: true).changeString(newValue);
},
),
);
}
}
class MyTextWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Text(Provider.of<Data>(context, listen: true).appName),
);
}
}
class MyAppBar extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Text(Provider.of<Data>(context, listen: true).appName);
}
}
class Data extends ChangeNotifier {
String appName = 'Understanding Provider';
void changeString(String newString) {
appName = newString;
notifyListeners();
}
}
Please somebody help, Thanks!

You should not listen to your provider when you update it inside MyTextField:
class MyTextField extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: TextField(
onChanged: (newValue) {
Provider.of<Data>(context, listen: false).changeString(newValue);
},
),
);
}
}
Set listen: false.

Related

How to change the text color which is in a different widget on switch with a flutter provider?

How to change the text color which is in a different widget on switch with a flutter provider?
When switch is on change text color to red else change to green. Bu don't merge first and second widgets.
When clicked switch button change other widget's text.
`
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(const SwitchApp());
class SwitchApp extends StatelessWidget {
const SwitchApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Switch Sample')),
body: const Center(
child: SwitchExample(),
),
),
);
}
}
class SwitchExample extends StatefulWidget {
const SwitchExample({super.key});
#override
State<SwitchExample> createState() => _SwitchExampleState();
}
class _SwitchExampleState extends State<SwitchExample> {
bool light = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
Switch(
// This bool value toggles the switch.
value: light,
activeColor: Colors.red,
onChanged: (bool value) {
// This is called when the user toggles the switch.
setState(() {
light = value;
});
},
),
MyText()
],
);
}
}
class MyText extends StatelessWidget {
const MyText({super.key});
#override
Widget build(BuildContext context) {
return const Text('Change my color',
style: TextStyle(color: Colors.green));
}
}
`
The easiest way would be to pass the color down into the constructor of MyText widget, since MyText widget is being built as a child of SwitchExample which is handling the switch state.
import 'package:provider/provider.dart';
void main() => runApp(const SwitchApp());
class SwitchApp extends StatelessWidget {
const SwitchApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Switch Sample')),
body: const Center(
child: SwitchExample(),
),
),
);
}
}
class SwitchExample extends StatefulWidget {
const SwitchExample({super.key});
#override
State<SwitchExample> createState() => _SwitchExampleState();
}
class _SwitchExampleState extends State<SwitchExample> {
bool light = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
Switch(
// This bool value toggles the switch.
value: light,
activeColor: Colors.red,
onChanged: (bool value) {
// This is called when the user toggles the switch.
setState(() {
light = value;
});
},
),
MyText(light ? Colors.green : Colors.blue)
],
);
}
}
class MyText extends StatelessWidget {
final Color color;
const MyText(this.color, {super.key});
#override
Widget build(BuildContext context) {
return Text('Change my color',
style: TextStyle(color: color));
}
}
But, if you wanted to use provider so that MyText could be a child widget anywhere below the provider widget in the tree you could use Provider with a ChangeNotifier:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(const SwitchApp());
class ColorModel extends ChangeNotifier {
Color color = Colors.green;
void setColor(Color color) {
this.color = color;
notifyListeners();
}
}
class SwitchApp extends StatelessWidget {
const SwitchApp({super.key});
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<ColorModel>(
create: (context) => ColorModel(),
child: MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Switch Sample')),
body: const Center(
child: SwitchExample(),
),
),
),
);
}
}
class SwitchExample extends StatefulWidget {
const SwitchExample({super.key});
#override
State<SwitchExample> createState() => _SwitchExampleState();
}
class _SwitchExampleState extends State<SwitchExample> {
bool light = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
Switch(
// This bool value toggles the switch.
value: light,
activeColor: Colors.red,
onChanged: (bool value) {
// This is called when the user toggles the switch.
setState(() {
light = value;
});
Provider.of<ColorModel>(context, listen: false)
.setColor(value ? Colors.green : Colors.blue);
},
),
MyText()
],
);
}
}
class MyText extends StatelessWidget {
const MyText({super.key});
#override
Widget build(BuildContext context) {
return Consumer<ColorModel>(builder: (context, state, _) {
return Text('Change my color', style: TextStyle(color: state.color));
});
}
}
Check out flutter's docs for more info: https://docs.flutter.dev/development/data-and-backend/state-mgmt/simple

I am trying provider, it seams everything fine, but the textField text is not updating in my text widget

I am practicing the state management. I am trying to update my textField (First class) to the TextWidgetwhich was in the Second() class. I don't know what problem i have made here?
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<Data>(
create: (context) => Data(),
child: MaterialApp(
home: First(),
),
);
}
}
My First Class
class First extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
TextField(
onChanged: (value) {
Provider.of<Data>(context).changeString(value);
},
),
Second(),
],
}
The Second Class
class Second extends StatelessWidget {
#override
Widget build(BuildContext context) {
return
Text(
Provider.of<Data>(context).text,
);
}
}
My Data class which extends ChangeNotifier
class Data extends ChangeNotifier {
String text = 'Type Something';
void changeString(String newText) {
text = newText;
notifyListeners();
}
}
Add a consumer to rebuild your widget with the new data.
Consumer<Data>(
builder: (context, value, _) => Text(
Provider.of<Data>(context).text,
);

Provider does not update Text widget

I have a problem with updating text widget in Level3 class after input some text in textField widget.
Appreciate your help
Expose data in below code:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
// home: TasksScreen(),
home: ChangeNotifierProvider<Data>(
create: (context) => Data(),
child: Scaffold(
appBar: AppBar(
title: Container(
child: MyText(),
),
),
body: Level1(),
),
),
);
}
}
create Data class for model data in below class
class Data extends ChangeNotifier {
String data = 'this data';
void changeString(String newString) {
data = newString;
print(newString);//don't print anything after typing in textFild
print(data);//don't print either
notifyListeners();
}
}
I want to use Data object properties in MyTextField in below code
but it seems do not trigger Provider.of<Data>(context).changeString(newValue)
class MyTextField extends StatelessWidget {
#override
Widget build(BuildContext context) {
return TextField(
onChanged: (newValue) {
print(newValue);//print newValue correctly
Provider.of<Data>(context).changeString(newValue);
},
);
}
}
class Level3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Text(
//this part don't update as typing in textField
Provider.of<Data>(context).data,
//will return an instance of Provider
//type found in the context that is sent as a parameter
),
),
);
}
}
You can copy paste run full code below
In onChanged, You can use listen: false
code snippet
return TextField(
onChanged: (newValue) {
print(newValue); //print newValue correctly
Provider.of<Data>(context, listen: false).changeString(newValue);
},
);
working demo
full code
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Data extends ChangeNotifier {
String data = 'this data';
void changeString(String newString) {
data = newString;
print(newString); //don't print anything after typing in textFild
print(data); //don't print either
notifyListeners();
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
// home: TasksScreen(),
home: ChangeNotifierProvider<Data>(
create: (context) => Data(),
child: Scaffold(
appBar: AppBar(
title: Container(
child: MyTextField(),
),
),
body: Level3(),
),
),
);
}
}
class MyTextField extends StatelessWidget {
#override
Widget build(BuildContext context) {
return TextField(
onChanged: (newValue) {
print(newValue); //print newValue correctly
Provider.of<Data>(context, listen: false).changeString(newValue);
},
);
}
}
class Level3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Text(
//this part don't update as typing in textField
Provider.of<Data>(context).data,
//will return an instance of Provider
//type found in the context that is sent as a parameter
),
),
);
}
}
void main() {
runApp(MyApp());
}
You can try this
(context.watch<Data>().data).toString();

Why can't I get the provider package to listen to a change?

So I'm writing a very simple app, just to learn a little bit more about the provider package in flutter. The app is this:
App functionallity
When I write in mi TextField, the text bellow it should change and the title should remain unchanged, but I can't get it to listen the changes from changeText method. The code is
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<Data>(
create: (context) => Data(),
child: MaterialApp(
home: Scaffold(
appBar: AppBar(
title: MyText(),
),
body: Container(
child: Level1(),
),
),
),
);
}
}
class Level1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Level2(),
);
}
}
class Level2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: <Widget>[
MyTextField(),
Level3(),
],
),
);
}
}
class Level3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Text(Provider.of<Data>(context, listen: false).data);
}
}
class MyText extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Text(context.watch<Data>().data);
}
}
class MyTextField extends StatelessWidget {
#override
Widget build(BuildContext context) {
return TextField(
onChanged: (newText) {
Provider.of<Data>(context).changeText(newText);
print(newText);
},
);
}
}
class Data extends ChangeNotifier {
String data = 'Hello';
void changeText(String newString) {
data = newString;
print('Hello $data');
notifyListeners();
}
}
Try putting the TextField widget inside a Stateful widget.
return TextField(
onChanged: (newText) {
Provider.of<Data>(context).changeText(newText);
print(newText);
},
);
Enclose this in a stateful widget
Also you have to add a Text controller to the textfield widget, go to docs for textfield widget in flutter.

Flutter : sending data across multiple screens

I have 3 Widget MyApp Widget ,Home Widget, and Sliver Appbar Widget, It's connected to each other. Example MyApp Widget -> Home Widget -> SliverAppbar Widget.
My question is , how to Passing data from My App Widget directly to SliverAppBar Widget ?
I found what i think it's can solve my case that is Inherited Widget. But i confused to understading to use this widget.
I already try using Inherited Widget as documentation like this :
MyApp Widget
class SettingsApp extends InheritedWidget {
SettingsApp({Key key, this.isDarkMode = false, Widget child})
: super(key: key, child: child);
final bool isDarkMode;
static SettingsApp of(BuildContext context) {
return (context.dependOnInheritedWidgetOfExactType<SettingsApp>());
}
#override
bool updateShouldNotify(SettingsApp oldWidget) {
return true;
}
}
SliverAppBar Widget
class SliverAppBarCustom extends StatelessWidget {
final Box detbBox = Hive.box("debt_box");
final UserModelHive userModelHive = Hive.box("user_box").get("userSession");
#override
Widget build(BuildContext context) {
final isDarkMode =
context.dependOnInheritedWidgetOfExactType<SettingsApp>().isDarkMode;
print(isDarkMode.toString());
var mediaQuery = MediaQuery.of(context);
var textTheme = Theme.of(context).textTheme;
return Text(isDarkMode.toString());
}
}
But i get this error :
Log
The following NoSuchMethodError was thrown building SliverAppBarCustom(dirty):
The getter 'isDarkMode' was called on null.
Receiver: null
Tried calling: isDarkMode
Using ScopedModel
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
void main() => runApp(MyApp());
class SettingsModel extends Model {
bool _isDarkMode;
SettingsModel({bool isDarkMode}) : _isDarkMode = isDarkMode ?? false;
bool get isDarkModel => _isDarkMode;
set isDarkModel(bool value) {
_isDarkMode = value;
notifyListeners();
}
void switchTheme() {
_isDarkMode = !_isDarkMode;
notifyListeners();
}
static SettingsModel of(BuildContext context) {
return ScopedModel.of<SettingsModel>(context);
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ScopedModel<SettingsModel>(
model: SettingsModel(isDarkMode: true),
child: MaterialApp(
home: InitPage(),
),
);
}
}
class InitPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Init Page")),
body: SizedBox.expand(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ScopedModelDescendant<SettingsModel>(
builder: (context, child, model) {
return Text('Is Dark Mode: ${model.isDarkModel}');
},
),
RaisedButton(
child: Text("Next Page"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondPage(),
),
);
},
),
],
),
),
);
}
}
class SecondPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Page"),
),
body: SizedBox.expand(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ScopedModelDescendant<SettingsModel>(
builder: (context, child, model) {
return Text('Is Dark Mode: ${model.isDarkModel}');
},
),
RaisedButton(
child: Text("Switch Theme"),
onPressed: SettingsModel.of(context).switchTheme,
),
],
),
),
);
}
}
Important: You should not change _isDarkModel without notifyListeners(). If you do UI may not update.