Flutter: Provider Value Not Updating - flutter

I am new to flutter/dart and am trying to get a value to update for a loading bar I would like to make. The value is updating within a provider widget I created, but when I try to utilize it, I am unable to figure out what is missing. Any assistance would be greatly appreciated.
class LoadingProvider extends ChangeNotifier{
int _downloadPercentage = 0;
int get downloadPercentage => _downloadPercentage;
void getPercentage(int input) {
_downloadPercentage = input;
notifyListeners();
print(downloadPercentage);
}
}
Here is the page I wish to display on
class ContourScreen extends StatefulWidget {
final Note note;
ContourScreen(this.note);
#override
State<StatefulWidget> createState() => new _ContourScreenState();
}
class _ContourScreenState extends State<ContourScreen> with TickerProviderStateMixin{
#override
void initState() {
super.initState();
}
#override
dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
final loadingProvider = Provider.of<LoadingProvider>(context);
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(),
body: Scrollbar(
child: SingleChildScrollView(
child: Container(
child: Padding(
padding: const EdgeInsets.only(left: 20.0, right: 20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ChangeNotifierProvider<ValueNotifier<int>>(
create: (context) => ValueNotifier<int>(0),
child: Text('${loadingProvider.downloadPercentage}'),),
],
),
),
),
),
),
);
}
}
Here is the git hub to all pages for reference https://github.com/mattcubs/loadinggif

Related

Flutter How to pass a parameter (int) to callbacks?

How can I pass a parameter wit callback in Flutter?
I have two files main.dart and block.dart. My goal is to add an int (12 for example) to myCallback in block.dart to use it in main.dart in the function whatToDo (instead of print ('Should receive the Value from myCallback');)
Here is the code of the main.dart File:
import 'package:flutter/material.dart';
import 'block.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MainBlock(),
);
}
}
class MainBlock extends StatefulWidget {
#override
_MainBlockState createState() => _MainBlockState();
}
class _MainBlockState extends State<MainBlock> {
void whatToDo() {
print('Should receive the Value from myCallback');
}
#override
Widget build(BuildContext context) {
// print(getraenke.asMap());
// print(getraenke.asMap().keys);
// print(getraenke);
return Scaffold(
body: Container(
margin: EdgeInsets.all(30.0),
color: Color(0xFF122C39),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: Block(
myCallback: whatToDo,
),
),
],
),
),
);
}
}
And here is the Code from block.dart with the callback:
import 'package:flutter/material.dart';
class Block extends StatelessWidget {
final Function myCallback;
Block({this.myCallback});
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(10.0),
color: Color(0xFF722662),
child: Center(
child: GestureDetector(
onTap: myCallback,
child: Text(
'Button',
style: TextStyle(
color: Color(0xFFFFFFFF),
fontSize: 22.0,
),
),
),
),
);
}
}
If I understood it correctly, You want your function to accept a parameter.
do it like this.
class Block extends StatelessWidget {
final Function(int num) myCallback;
Block({this.myCallback});
and when you call it, you provide it with the parameter
GestureDetector(
onTap:()=> myCallback(12),
child: ...
and finally you can access it from your main
void whatToDo(int num) {
print(num);
}
Simple way without any advanced topic. Better read some articles about state management. Official documentation.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MainBlock(),
);
}
}
class MainBlock extends StatefulWidget {
#override
_MainBlockState createState() => _MainBlockState();
}
class _MainBlockState extends State<MainBlock> {
void whatToDo(int value) {
print('Should receive the Value from myCallback');
print(value);
}
#override
Widget build(BuildContext context) {
// print(getraenke.asMap());
// print(getraenke.asMap().keys);
// print(getraenke);
return Scaffold(
body: Container(
margin: EdgeInsets.all(30.0),
color: Color(0xFF122C39),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: Block(
myCallback: whatToDo,
),
),
],
),
),
);
}
}
class Block extends StatelessWidget {
final void Function(int) myCallback;
Block({required this.myCallback});
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(10.0),
color: Color(0xFF722662),
child: Center(
child: GestureDetector(
onTap: ()=>myCallback(12),
child: Text(
'Button',
style: TextStyle(
color: Color(0xFFFFFFFF),
fontSize: 22.0,
),
),
),
),
);
}
}
in main.dart:
void whatToDo(int value) {
print('the value is $value');
}
in block.dart:
class Block extends StatelessWidget {
final ValueChanged<int> myCallback;
Block({required this.myCallback});
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(10.0),
color: const Color(0xFF722662),
child: Center(
child: GestureDetector(
onTap: () => myCallback(100),
child: const Text(
'Button',
style: TextStyle(
color: Color(0xFFFFFFFF),
fontSize: 22.0,
),
),
),
),
);
}
}
I add more info to Ethsan Askari's answer.
You can define your custom callback and reuse it throughout your app.
typedef OnWhatToDoCallback = Function(int value);
class Block extends StatefulWidget {
const Block({
Key? key,
required this.onWhatToDo,
}) : super(key: key);
final OnWhatToDoCallback onWhatToDo;
...
}

How do I get rid of this overflowed pixels error in Flutter Web

I have a navigationbar in flutter web and I am getting a render issue. It works fine when I am full screen, but It shows the error once I minimized the page to almost mobile version.
This is my code:
'''
class NavigationBar extends StatefulWidget {
const NavigationBar({Key key}) : super(key: key);
#override
_NavigationBarState createState() => _NavigationBarState();
}
class _NavigationBarState extends State<NavigationBar> {
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.fromLTRB(10, 0, 20, 0),
width: MediaQuery.of(context).size.width,
color: kNavBarBlueBackground,
height: 100,
child: Flexible(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(width: 20),
Image.asset('assets/img/image_logo.png'),
Spacer(),
Row(
children: [
_NavBarItem('Inicio'),
SizedBox(width: 35),
_NavBarItem('Agencias'),
'''
This is the error I am getting.
Edited this with more code because user below asked for code where this navigation bar was placed in code:
'''
lass _ViajeroExpresoPageState extends State<ViajeroExpresoPage> {
void openDrawer() {
_drawerKey.currentState.openDrawer();
}
#override
Widget build(BuildContext context) {
return ResponsiveBuilder(
builder: (context, sizingInformation) {
// Check the sizing information here and return your UI
if (sizingInformation.deviceScreenType == DeviceScreenType.desktop) {
return Desktop();
}
if (sizingInformation.deviceScreenType == DeviceScreenType.tablet) {
return Mobile(openDrawer: openDrawer);
}
return Mobile(openDrawer: openDrawer);
},
);
}
}
class Desktop extends StatelessWidget {
final Function openDrawer;
const Desktop({Key key, this.openDrawer}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
key: _drawerKey,
body: Stack(
children: [
ListView(
children: [
Container(
width: MediaQuery.of(context).size.width,
color: Colors.blue,
child: NavigationBar(),
),
'''
try wrapping the listview widget in 'expanded' widget

Flutter: How To Change/Refresh State From Another Widget

I am trying to have a global integer that is displayed in a widget and then is updated by something (a button click or something) from another widget. All of the other ways i have tried don't work. What is the best way to do this?
Stack overflow says i have too much code so more text more text more text
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: Scaffold(
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ScoreDisplay(),
PointButton(),
],
),
),
),
),
);
}
int score = 0;
class ScoreDisplay extends StatefulWidget {
#override
_ScoreDisplayState createState() => _ScoreDisplayState();
}
class _ScoreDisplayState extends State<ScoreDisplay> {
#override
Widget build(BuildContext context) {
return Center(
child: Container(
child: Text(
'Score: $score',
),
),
);
}
}
class PointButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Container(
child: RaisedButton(
//onPressed: //increment score,
),
),
);
}
}
You need to implement some kind of State Management for that.
Here are two basic ways to implement such a feature: with a StatefulWidget and with Riverpod.
1. With a StatefulWidget
I extracted your Scaffold as a StatefulWidget maintaining the score of your application.
I then use ScoreDisplay as a pure StatelessWidget receiving the score as a parameter. And your PointButton is also Stateless and call the ScorePage thanks to a simple VoidCallback function.
Full source code:
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: ScorePage(),
),
);
}
class ScorePage extends StatefulWidget {
const ScorePage({
Key key,
}) : super(key: key);
#override
_ScorePageState createState() => _ScorePageState();
}
class _ScorePageState extends State<ScorePage> {
int score = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ScoreDisplay(score: score),
PointButton(onIncrement: () => setState(() => score++)),
],
),
),
);
}
}
class ScoreDisplay extends StatelessWidget {
final int score;
const ScoreDisplay({Key key, this.score}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: Container(
child: Text(
'Score: $score',
),
),
);
}
}
class PointButton extends StatelessWidget {
final VoidCallback onIncrement;
const PointButton({Key key, this.onIncrement}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: Container(
child: ElevatedButton(
onPressed: () => onIncrement?.call(),
child: Text('CLICK ME'),
),
),
);
}
}
2. With Riverpod
Create a StateProvider:
final scoreProvider = StateProvider<int>((ref) => 0);
Watch the StateProvider:
final score = useProvider(scoreProvider).state;
Update the StateProvider
context.read(scoreProvider).state++
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(
home: Scaffold(
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ScoreDisplay(),
PointButton(),
],
),
),
),
),
),
);
}
int score = 0;
class ScoreDisplay extends HookWidget {
#override
Widget build(BuildContext context) {
final score = useProvider(scoreProvider).state;
return Center(
child: Container(
child: Text(
'Score: $score',
),
),
);
}
}
class PointButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Container(
child: ElevatedButton(
onPressed: () => context.read(scoreProvider).state++,
child: Text('CLICK ME'),
),
),
);
}
}
final scoreProvider = StateProvider<int>((ref) => 0);
Check Riverpod Website for more info and more advanced use cases.
But you have many more flavors of State Management available.
The best example is to use "provider" package which can be found on www.pub.dev
It is very easy state management package that can help You solve this problem. Keep in my that provider instead of setState() uses notifyListener()

how to change the state of one statefulwidget from another in flutter?

I have a stateful widget which is conditionally rendering two childs inside stack, and i want to change the condition of the rending from a third child . any idea ?
Parent code :
class MapPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body:Body()
);
}
}
class Body extends StatefulWidget {
final String showScreen;
const Body({
Key key,
this.showScreen="post",
}) : super(key:key);
#override
_BodyState createState() => _BodyState();
}
class _BodyState extends State<Body> {
Widget _conditionedWidget(){
if(this.widget.showScreen=="map"){
return MapScreen();
}else if(this.widget.showScreen == "post"){
return PostScreen();
}
}
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
DrawerScreen(),
_conditionedWidget(),
],
);
}
}
child code
class DrawerScreen extends StatefulWidget {
#override
_DrawerScreenState createState() => _DrawerScreenState();
}
class _DrawerScreenState extends State<DrawerScreen> {
#override
Widget build(BuildContext context) {
return Container(
color:kPrimaryColor,
padding:EdgeInsets.only(top:70),
child:Column(
children: <Widget>[
Row(
children: <Widget>[
SizedBox(width:20.0),
CircleAvatar(),
SizedBox(width:10.0),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Biswas Sampad',style:TextStyle(
color:Colors.white,
fontWeight: FontWeight.bold,
fontSize: 20.0,
)),
Text('#biswassampad',style:TextStyle(
color:Colors.grey[200],
fontSize: 15.0,
))
],
)
],
),
Container(
padding: EdgeInsets.symmetric(horizontal: 20,vertical:20),
margin: EdgeInsets.symmetric(vertical:30),
child: Column(
children: <Widget>[
MenuButton(icon:Icons.style, name:'Explore',action:(){
print('showing maop');
}),
MenuButton(icon:Icons.tag_faces, name:'Profile',action:(){
print('showing profile');
}),
MenuButton(icon:Icons.people, name:'People',action:(){
print('showing People');
}),
MenuButton(icon:Icons.speaker_notes, name:'Messages',action:(){
print('showing messages');
}),
MenuButton(icon:Icons.notifications, name:'Notifications',action:(){
print('showing Notifications');
}),
MenuButton(icon:Icons.satellite,name:'Settings',action:(){
print('showing settings');
})
],
),
),
LogoutSection()
],
)
);
}
}
So basically i want to change the showScreen value of the parent widget from DrawerScreen>MenuButton>action ?
any idea how to do it !! Thanks in advance.
You can use the Function in "DrawerScreen" widget like this :
write this code into the header of the class :
final Function onChangeState = Function();
DrawerScreen({#rquired onChangeState});
and in MenuButton call onChangeState function , like this:
MenuButton(icon:Icons.satellite,name:'Settings',action:(){
widget.onChangeState("Settings");
})
and change old code in Body widget to :
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
DrawerScreen(onChangeState : (newState){
setState(){
this.widget.showScreen = newState;
};
}),
_conditionedWidget(),
],
);
}

How to properly reuse a Provider in Flutter

So I have this ChangeNotifierProvider high in my widget tree as I am seeing many children widgets to listen to its value.
What I am currently doing is that I pass down the Provider.of(context) object from the parent widget into it's children via constructors whenever I am to reuse some values/functions on my children widgets. For example, everytime I create a Provider.of(context) object for my children widgets, it seems that it does not carry over the updated values I have on the Parent Provider but rather this one has my default null/0/'empty' ones like it has only been created. This lead me to pass down the initial Provider.of(context) object to each children that will use the updated values and functions of the ChangeNotifier.
This setup is working for me, however, when my Widget Tree has started being complex, I am constantly passing down values through each widget and to some that do not even use it at all just for its children to listen to the main provider.
I think what I may be doing now is anti-pattern of the Provider Architecture, I am hoping you guys can help me on a more optimized and efficient way of doing this.
Thank you very much!
P.S. There are some things in the documentation that I am not yet quite grasping properly.
Edits Below to include sample code and visualization:
provider_type.dart
class ProviderType extends ChangeNotifier{
String valueA = '';
String valueB = '';
}
home.dart
import ..provider_type.dart
...
Widget build(BuildContext context){
return ChangeNotifierProvider<ProviderType>(
create: (context) => ProviderType(),
child: ScreenColumn();
);
}
...
screen_column.dart
import ..screen_a.dart
import ..screen_b.dart
class ScreenColumn extends StatelessWidget{
Widget build(BuildContext context){
var providerType = Provider.of<ProviderType>(context);
return Column(
children: <Widget>[
ScreenA(providerType: providerType),
ScreenB(providerType: providerType),
],
);
}
}
screen_a.dart
class ScreenA extends StatelessWidget{
final ProviderType providerType;
ScreenA({this.providerType});
Widget build(BuildContext context){
return Text(
'${providerType.valueA}'
);
}
}
screen_b.dart
import ..screen_c.dart
class ScreenB extends StatelessWidget{
final ProviderType providerType;
ScreenB({this.providerType});
Widget build(BuildContext context){
return ScreenC(providerType: providerType);
}
}
screen_c.dart
class ScreenC extends StatelessWidget{
final ProviderType providerType;
ScreenB({this.providerType});
Widget build(BuildContext context){
return Column(
children: <Widget>[
Text(
'${providerType.valueA}'
)
Text(
'${providerType.valueB}'
)
Text(
'${providerType.valueC}'
)
]
);
}
}
Visualization
So what I am currently doing is to pass down the object providerType from ScreenColumn to Screens A, B, and C just so each of them have the same "Source of Values". Cause when I try to make different Provider.of objects and use them, they do not share the same updated values when I do some computation.
Is there something I can do to make this more efficient or is there a better way that I need to do?
To those who may be wondering or are searching for answers to the same question, look at my sample code below that shows how you can reuse/share your Provider Values and Functions at any point in your widget tree as long as they are under your Parent Provider.
And yes, you can actually just create Provider.of Objects anywhere in
your tree without passing down the initial Provider.of object that you
have created.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class ProviderType extends ChangeNotifier {
String value = DateTime.now().toString();
changeValue() {
value = DateTime.now().toString();
notifyListeners();
}
}
void main() => runApp(AppIndex());
class AppIndex extends StatelessWidget {
const AppIndex({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<ProviderType>(
create: (context) => ProviderType(),
child: MaterialApp(
home: Home(),
),
);
}
}
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
var providerType = Provider.of<ProviderType>(context);
return Scaffold(
appBar: AppBar(
title: Text('Sample App'),
),
body: ScreenColumn(),
floatingActionButton: FloatingActionButton.extended(
onPressed: () => providerType.changeValue(),
label: Text('ChangeValue'),
),
);
}
}
class ScreenColumn extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ScreenA(),
ScreenB(),
ScreenC(),
ScreenC(),
],
));
}
}
class ScreenA extends StatelessWidget {
#override
Widget build(BuildContext context) {
var providerType = Provider.of<ProviderType>(context);
return Card(
color: Colors.red,
elevation: 8.0,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(providerType.value),
),
);
}
}
class ScreenB extends StatelessWidget {
#override
Widget build(BuildContext context) {
var providerType = Provider.of<ProviderType>(context);
return Card(
color: Colors.blue,
elevation: 8.0,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(providerType.value),
ScreenC(),
ScreenC(),
],
),
),
);
}
}
class ScreenC extends StatelessWidget {
#override
Widget build(BuildContext context) {
// var providerType = Provider.of<ProviderType>(context);
return Card(
color: Colors.green,
elevation: 8.0,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text('This is Screen B with no Provider.of Object'),
ScreenD(),
ScreenD(),
ScreenD(),
],
),
),
);
}
}
class ScreenD extends StatelessWidget {
#override
Widget build(BuildContext context) {
var providerType = Provider.of<ProviderType>(context);
return Card(
color: Colors.yellow,
elevation: 8.0,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
'This is Screen D. A Provider.of object was created here without inheriting the Parent\'s Provider.of object.'),
Text(providerType.value),
],
),
),
);
}
}