calling setState from drawer? - flutter

Context
Using the standard flutter demo I added a drawer. I put the contents of my drawer in another class in another file. Both are stateful widgets. I use a floating action button in the drawer with setState incrementing the global variable for the number shown on the main screen.
What happens
When I press it nothing happens.
It does not update the text on the main page/main.dart until I use the floating action button on the main page/main.dart. Then it adds all the increments I added in the drawer too.
So it's just not rebuilding the widget.
How do I get it to rebuild the widget? I thought everything you needed was that they were both inside a setstate?
Best possible cause I have come up with
Is it because even though I use a stateful widget inside a stateful widget, the setstate only works on the embedded stateful widget because the embedded widget is technically a created object in the main.dart?
Code for main.dart
import 'package:flutter/material.dart';
import 'drawer.dart';
DrawerClass _drawer = DrawerClass();
int counter = 0;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void _incrementCounter() {
setState(() {
counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
endDrawer: SafeArea(child: Drawer(child: Container(child: _drawer,),)),
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Code for drawer
import 'package:flutter/material.dart';
import 'main.dart';
class DrawerClass extends StatefulWidget {
#override
_DrawerClassState createState() => _DrawerClassState();
}
class _DrawerClassState extends State<DrawerClass> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFF222831),
body: Container(
width: 100,
child: Center(
child: FloatingActionButton(
tooltip: 'Increment',
child: Icon(Icons.add),
onPressed: () {
setState(() {
counter++;
});
},
),
),
),
);
}
}

Just pass the _incrementCounter to the DrawerClass. With this change your DrawerClass can now be a StatelessWidget and there will be no need for the counter and _drawer variables to be global. Please see the code below :
main.dart
import 'package:flutter/material.dart';
import 'drawer.dart';
//DrawerClass _drawer = DrawerClass();
//int counter = 0;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int counter = 0;
void _incrementCounter() {
setState(() {
counter++;
});
}
#override
Widget build(BuildContext context) {
final DrawerClass _drawer = DrawerClass(
increment: _incrementCounter,
);
return Scaffold(
endDrawer: SafeArea(
child: Drawer(
child: Container(
child: _drawer,
),
)),
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
drawer.dart
import 'package:flutter/material.dart';
class DrawerClass extends StatelessWidget {
final Function increment;
const DrawerClass({Key key, this.increment}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFF222831),
body: Container(
width: 100,
child: Center(
child: FloatingActionButton(
tooltip: 'Increment',
child: Icon(Icons.add),
onPressed: increment,
),
),
),
);
}
}

Related

Flutter: buttons not showing while emulation

I try to make simple app with counter button. Everything seems allright before i try to emulate this - button do not show while emulation. Maybe problem with extends between classes?
import 'package:flutter/material.dart';
void main() {
runApp(const CountApp());
}
class CountApp extends StatelessWidget {
#immutable
const CountApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
backgroundColor: Color.fromRGBO(255, 215, 64, 1),
title: Text("Count is here^_^"),
titleTextStyle: TextStyle(
color: Colors.indigo, fontSize: 35, fontWeight: FontWeight.w700),
centerTitle: true,
),
),
);
}
}
class CounterWidget extends StatefulWidget {
#override
_CounterWidgetState createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _counter = 50;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('widget.title'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Increment',
),
Text(
'$_counter',
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add_rounded),
),
);
}
}
Can't see where is my mistake, only header showing while i trying to emulate
Can anyone say what i do wrong, please?
check two line if you add counter in appbar:
CountApp class add this one:
home: CounterWidget()
also in CounterWidget class add this one
title: Text('widget.title '+'$_counter'),
full code solution:
import 'package:flutter/material.dart';
void main() {
runApp(const CountApp());
}
class CountApp extends StatelessWidget {
#immutable
const CountApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
//here you add class
home: CounterWidget()
);
}
}
class CounterWidget extends StatefulWidget {
#override
_CounterWidgetState createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _counter = 50;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
//title here add your counter
title: Text('widget.title '+'$_counter'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Increment',
),
Text(
'$_counter',
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add_rounded),
),
);
}
}
You are not calling CounterWidget widget in material app
import 'package:flutter/material.dart';
void main() {
runApp(const CountApp());
}
class CountApp extends StatelessWidget {
#immutable
const CountApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: CounterWidget() // this line
),
);}

ListView.builder inside a Column inside a SingleChildScrollView

all. I am trying to add a ListView.builder to a Column that is inside a SingleChildScrollView. However, I am getting an exception, likely due to the fact that there is no constraint for the ListView.builder. Here is my code:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ListView.builder(
itemBuilder: (context, index) => const Text('a'),
itemCount: 2,
),
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
If I use a Container and set a defined height, the code above works. However, I am trying to get the ListView.builder to not have a fixed size. I've tried using the Expanded widget and I still get this error. Is there a way to make this work without a defined height? Thanks
In the column widget add mainAxisSize:MainAxisSize.min and in List view.builder add shrinkWrap:true and physics:NeverScrollablePhysics(). That should solve the issue and instead of center widget use SafeArea or a container with specific height.
Here's your working code
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ListView.builder(
shrinkWrap:true,// -> Add this here
physics:NeverScrollablePhysics(),// -> And this one
itemBuilder: (context, index) => const Text('a'),
itemCount: 2,
),
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}

Navigation problem using GetX package in flutter

I am using the GetX package for the navigation purpose in flutter project but it is not working for me. It is showing some problems in debug console also - 'Unexpected format,
you can only use widgets and widget functions here'.
Below is my main.dart file-
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:jarvis/networking/login.dart';
void main() {
runApp(const MyApp());
//Get.to(Login());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Flutter Demo',
theme: ThemeData('Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _loginScreen(){
Get.to(Login());
}
void _incrementCounter() {
setState(() {
_counter++;
//Get.to(Login());
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
TextButton(onPressed: _loginScreen, child: const Text("Press here to go to next page", style: TextStyle(fontSize: 20),)),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
Whenever the user will click on the Text button, it is supposed that the login screen will appear but there is no change in the UI.
Below is my login_screen.dart file -
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:dio/dio.dart';
class Login extends StatelessWidget {
const Login({ Key? key }) : super(key: key);
#override
Widget build(BuildContext context) {
return const Material(
child: GetMaterialApp(
title: "Login Screen",
home: GetOtp(),
),
);
}
}
class GetOtp extends StatefulWidget {
const GetOtp({ Key? key }) : super(key: key);
#override
_GetOtpState createState() => _GetOtpState();
}
class _GetOtpState extends State<GetOtp> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Login"),
),
body: const Center(
child: Text("hi here ankit", style: TextStyle(color: Colors.red, fontSize: 20),),
),
);
}
}
I tried but unable to find the mistake.
In your main.dart file, you can directly add this:
void main() {
runApp(const Login());
}

How to pass variables between 2 different files and manipulate them

I am learning flutter, and I would like to know how to pass a variable or a method between 2 different files (to add additional widgets).
In my example, I took the code provided by flutter when we create a new project, to this code, I added a second file called "second.dart" in which I get the variable "_counter" in the file main "main.dart" which I multiply by 10.
main.dart
import 'package:flutter/material.dart';
import 'second.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
Container(
child: Second(),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
second.dart
import 'package:flutter/material.dart';
import 'main.dart';
class Second extends StatefulWidget {
#override
_SecondState createState() => _SecondState();
}
class _SecondState extends State<Second> {
int counter10 = _MyHomePageState._counter * 10;
#override
Widget build(BuildContext context) {
return Container(
color: Colors.red,
width: 200,
height: 300,
child: Text('My increment * 10 : $counter10 '),
);
}
}
however, I have this error."_MyHomePageState" is highlighted in red.
lib/second.dart:11:18: Error: Getter not found: '_MyHomePageState'.
var counter10 =_MyHomePageState._counter * 10;
Thank you for your help
See this isn't the way how we pass variables to the other files or other widgets. To create a widget you need to choose between stateful or stateless if you want to manipulate state from inside of the Second class declare it as a stateful but in your case you need to have a stateless widget.
For Example this:
class Second extends StatelessWidget {
final int counter;
const Second({Key key, this.counter}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
color: Colors.red,
width: 200,
height: 300,
child: Text('My increment * 10 : ${counter * 10} '));
}
}
And Pass this variable from your first that is like this :
import 'package:flutter/material.dart';
import 'second.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
Container(
child: Second(counter:_counter),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
Note : You can share normal variables by referring them across classes but they shouldn't be private eg have an _ in front of them. Another thing even if you share your variable it wont have any effect on the Second Page directly as flutter doesn't know that it is changing and rather just treat it as an instance.
You can share static data using classes:
example
class AppStrings {
static Color appColor = Colors.blue;
}
And later refer to it as AppStrings.appColor
There are some points that you should do in your codes:
when we use underscore as the first character of the variable name, it means that this variable is private! so you can not use int counter10 = _MyHomePageState._counter * 10; in the second widget!
if you want to pass variables to the child widget, you need to create a constructor as follow:
class Second extends StatefulWidget {
int counter;
Second(this.counter);
#override
_SecondState createState() => _SecondState();
}
now you can pass _countre variable to the Second widget:
Container(
child: Second(_counter),
),
please let me know if there is any problem or error.

Why is Flutter NotificationListener not catching my notifications?

I am having a problem with the NotificationListener in flutter. I've build a simple testing app because I am struggling with it.
After clicking on the FlatButton the Notification should be dispatched and then caught by the NotificationListener in onNotification.
So the expected console output would be:
"TestNot"
"Bubble"
But all I am getting is "TestNot".
So the notification is not caught by the listener.
Any idea what I could be doing wrong?
Thank you :-)
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyNotification extends Notification {
final String title;
const MyNotification({this.title});
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: NotificationListener<MyNotification>(
onNotification: (notification) {
print("Bubble");
return true;
},
child: Center(
child: Column(
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
FlatButton(onPressed: () {print("TestNot"); MyNotification(title: "TestNot").dispatch(context);}, child: Text("TestNot")),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
)),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
When you need a child to notify its parent, you can use NotificationListener.
But, when you need communications to be implemented inversely, in other words, a parent to notify its children, you can use ValueListenableBuilder
A nice doc about it available here:
https://medium.com/flutter-community/flutter-notifications-bubble-up-and-values-go-down-c1a499d22d5f
"Flutter, notifications ‘bubble up’ and values ‘go down’"
You cannot receive the notification at the same level of where it was dispatched. Please refer to docs : https://api.flutter.dev/flutter/widgets/NotificationListener-class.html
NotificationListener class :
A widget that listens for Notifications bubbling up the tree.
I've updated your code to make it work.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyNotification extends Notification {
final String title;
const MyNotification({this.title});
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: NotificationListener<MyNotification>(
onNotification: (MyNotification notification) {
print("Bubble");
return true;
},
child: Center(
child: Column(
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
MyChild(),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
)),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class MyChild extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FlatButton(
onPressed: () {
print("TestNot");
MyNotification(title: "TestNot").dispatch(context);
},
child: Text("TestNot"));
}
}