set state Badge appbar - flutter - flutter

I'm trying to increment the app bar icon by making a custom app bar. But I can't update the value with setState. I tried to update the value by the setAppBarValue function, but it didn't work.
MyAppBar.of(context)?.setAppBarValue(_counter);
import 'package:badges/badges.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
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: MyAppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
_incrementCounter();
MyAppBar.of(context)?.setAppBarValue(_counter);
},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
class MyAppBar extends StatefulWidget implements PreferredSizeWidget {
MyAppBar() : super();
#override
Size get preferredSize => const Size.fromHeight(60);
#override
_MyAppBarState createState() => _MyAppBarState();
static _MyAppBarState? of(BuildContext context) =>
context.findAncestorStateOfType<_MyAppBarState>();
}
class _MyAppBarState extends State<MyAppBar>
with SingleTickerProviderStateMixin {
int _appBarValue = 0;
setAppBarValue(int value) {
setState(() {
_appBarValue = value;
});
}
#override
Widget build(BuildContext context) {
return AppBar(
title: const Text("teste"),
actions: <Widget>[Center(child: _alertBadge(context))]);
}
Widget _alertBadge(BuildContext context) {
return Badge(
position: BadgePosition.topEnd(
top: 0,
end: 3,
),
badgeStyle: BadgeStyle(
badgeColor: Colors.red,
),
badgeContent: Text(
_appBarValue.toString(),
style: TextStyle(color: Colors.white),
),
child:
IconButton(icon: Icon(Icons.shopping_bag_outlined), onPressed: () {}),
);
}
}

You Have to pass the increment variable value to the appbar widget like this:
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
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: MyAppBar(
appBarValue: _counter,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
_incrementCounter();
// MyAppBar.of(context)?.setAppBarValue(_counter);
},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
And use that value in your app bar widget:
class MyAppBar extends StatefulWidget implements PreferredSizeWidget {
const MyAppBar({
Key? key,
required this.appBarValue,
}) : super(key: key);
final int appBarValue;
#override
Size get preferredSize => const Size.fromHeight(60);
#override
_MyAppBarState createState() => _MyAppBarState();
static _MyAppBarState? of(BuildContext context) =>
context.findAncestorStateOfType<_MyAppBarState>();
}
class _MyAppBarState extends State<MyAppBar>
with SingleTickerProviderStateMixin {
// int _appBarValue = 0;
//
// setAppBarValue(int value) {
// setState(() {
// _appBarValue = value;
// });
// }
#override
Widget build(BuildContext context) {
return AppBar(
title: const Text("teste"),
actions: <Widget>[Center(child: _alertBadge(context))]);
}
Widget _alertBadge(BuildContext context) {
return Badge(
position: BadgePosition.topEnd(
top: 0,
end: 3,
),
badgeStyle: BadgeStyle(
badgeColor: Colors.red,
),
badgeContent: Text(
widget.appBarValue.toString(),
style: TextStyle(color: Colors.white),
),
child:
IconButton(icon: Icon(Icons.shopping_bag_outlined), onPressed: () {}),
);
}
}

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
),
);}

Flutter - How to change button color on the click and disable other buttons?

I have a listview with several green buttons, and I need to change the color of a button to red on click. The problem is that in doing that all the other buttons need to go back to their base color green.
On this example below (working version at https://www.dartpad.dev/?id=b4ea6414b6a4ffcc7135579e673be845) All buttons change the color on click independently of the other buttons, but the desired effect is that all the other buttons should be green when the clicked button is red.
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
MyWidget(
text: 'Button 1',
onPressed: () => print('Click'),
),
MyWidget(
text: 'Button 2',
onPressed: () => print('Click'),
),
MyWidget(
text: 'Button 3',
onPressed: () => print('Click'),
),
MyWidget(
text: 'Button 4',
onPressed: () => print('Click'),
),
],
)),
),
);
}
}
class MyWidget extends StatefulWidget {
const MyWidget({
Key? key,
required this.text,
required this.onPressed,
}) : super(key: key);
#override
State<MyWidget> createState() => _MyWidgetState();
final String text;
final VoidCallback onPressed;
}
class _MyWidgetState extends State<MyWidget> {
bool isFavourte = false;
#override
Widget build(BuildContext context) {
return ElevatedButton(
style: ElevatedButton.styleFrom(
primary: isFavourte ? Colors.red : Colors.green,
),
onPressed: () {
setState(() => isFavourte = !isFavourte);
widget.onPressed();
},
child: Text(widget.text));
}
}
How this can be done?
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
MyAppState createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
final selectedIndexNotifier = ValueNotifier<int?>(null);
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: ValueListenableBuilder<int?>(
valueListenable: selectedIndexNotifier,
builder: (_, selectedIndex, __) => Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (int i = 1; i <= 4; i++)
MyWidget(
key: ValueKey(i),
text: 'Button $i',
isFavorite: selectedIndex == i,
onPressed: () => selectedIndex == i ? selectedIndexNotifier.value = null : selectedIndexNotifier.value = i
)
],
))),
),
);
}
}
class MyWidget extends StatelessWidget {
const MyWidget({
Key? key,
required this.text,
required this.isFavorite,
required this.onPressed,
}) : super(key: key);
#override
Widget build(BuildContext context) => ElevatedButton(
style: ElevatedButton.styleFrom(
primary: isFavorite ? Colors.red : Colors.green,
),
onPressed: onPressed,
child: Text(text));
final String text;
final bool isFavorite;
final VoidCallback onPressed;
}
Here's an example doing exactly what you want to achieve, by saving the state of each button on a List and updating them all as one changes:
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
List<Map> buttonList = [
{
'label': 'button1',
'active': true,
},
{
'label': 'button2',
'active': true,
},
{
'label': 'button3',
'active': true,
},
{
'label': 'button4',
'active': true,
},
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: buttonList.length,
itemBuilder: (context, index){
return ElevatedButton(
onPressed: () => onPressed(buttonList[index]),
style: ButtonStyle(
backgroundColor: buttonList[index]['active']
? MaterialStateProperty.all(Colors.green)
: MaterialStateProperty.all(Colors.red),
),
child: Text(buttonList[index]['label']),
);
},
),
);
}
void onPressed(Map button){
setState(() {
for (var element in buttonList) {
element['active'] = false;
}
button['active'] = true;
});
}
}
created selectedValue variable in myWidget2 and id for evrey button so when ever you press a button it going to set selectedValue = id so that only the button whit the id = selectedValue going to turn red
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
int selectedValue = 0 ;
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: MyWidget2() ,
);
}
}
class ValueChanged extends Notification {
final int selectedValue ;
ValueChanged(this.selectedValue);
}
class MyWidget2 extends StatefulWidget {
const MyWidget2({
Key? key,
}) : super(key: key);
#override
State<MyWidget2> createState() => _MyWidget2State();
}
class _MyWidget2State extends State<MyWidget2> {
int selectedValue = 0 ;
#override
Widget build(BuildContext context) {
return Scaffold(
body: NotificationListener<ValueChanged>(
onNotification: (n) {
setState(() {
selectedValue = n.selectedValue ;
// Trigger action on parent via setState or do whatever you like.
});
return true;
},
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
MyWidget(
text: 'Button 1',
onPressed: () => print('Click'),
id: 1,
selectedValue :selectedValue ,
),
MyWidget(
text: 'Button 2',
onPressed: () => print('Click'),
id: 2,
selectedValue :selectedValue ,
),
MyWidget(
text: 'Button 3',
onPressed: () => print('Click'),
id:3,
selectedValue :selectedValue ,
),
MyWidget(
text: 'Button 4',
onPressed: () => print('Click'),
id:4,
selectedValue :selectedValue ,
),
],
)),
),
);
}
}
class MyWidget extends StatefulWidget {
const MyWidget({
Key? key,
required this.text,
required this.onPressed,
required this.id,
required this.selectedValue,
}) : super(key: key);
#override
State<MyWidget> createState() => _MyWidgetState();
final int id;
final String text;
final VoidCallback onPressed;
final int selectedValue ;
}
class _MyWidgetState extends State<MyWidget> {
#override
Widget build(BuildContext context) {
return ElevatedButton(
style: ElevatedButton.styleFrom(
primary: widget.id == widget.selectedValue ? Colors.red : Colors.green,
),
onPressed: () {
setState(() => ValueChanged(widget.id).dispatch(context));
widget.onPressed();
},
child: Text(widget.text));
}
}
First I've made MyWidget Stateless and Create Two new things:
ButtonData Class: Separate The Actual data that needs to be controlled and makes it scalable.
MyButtonList: StatefulWidget that contains a List of boolean values to track the current active Button
here's an example:
Create new file and copy the following code and see the result:
class TestPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: Colors.blue[800],
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyButtonList(
buttons: [
ButtonData(text: 'Test'),
ButtonData(text: 'Test'),
ButtonData(text: 'Test'),
ButtonData(text: 'Test'),
ButtonData(text: 'Test'),
],
),
),
),
);
}
}
class MyButtonList extends StatefulWidget {
const MyButtonList({Key? key, required this.buttons}) : super(key: key);
final List<ButtonData> buttons;
#override
State<MyButtonList> createState() => _MyButtonListState();
}
class _MyButtonListState extends State<MyButtonList> {
late List<bool> favoriateState;
#override
void initState() {
favoriateState = List.generate(
widget.buttons.length, (index) => widget.buttons[index].isFavorite);
super.initState();
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (var i = 0; i < widget.buttons.length; i++)
MyWidget(
text: widget.buttons[i].text,
onPressed: () {
for (var j = 0; j < favoriateState.length; j++) {
favoriateState[j] = false;
}
setState(() {
favoriateState[i] = true;
if (widget.buttons[i].onPressed != null) {
widget.buttons[i].onPressed!();
}
});
},
isFavourte: favoriateState[i],
),
],
);
}
}
class ButtonData {
final String text;
final Function()? onPressed;
final bool isFavorite;
ButtonData({required this.text, this.onPressed, this.isFavorite = false});
}
class MyWidget extends StatelessWidget {
const MyWidget(
{Key? key,
required this.text,
required this.onPressed,
this.isFavourte = false})
: super(key: key);
final String text;
final Function()? onPressed;
final bool isFavourte;
#override
Widget build(BuildContext context) {
return ElevatedButton(
style: ElevatedButton.styleFrom(
primary: isFavourte ? Colors.red : Colors.green,
),
onPressed: onPressed,
child: Text(text));
}
}
You seem to want to emulate RadioButtons by using TextButtons.
Withing a group of RadioListTile-s only one can be active. And this is what you want to achieve, if I understood you correctly.
May I suggest to use RadioListTile-s instead and then style (or theme) these as you like: Green for inactive Tiles, Red for active Tiles.
The following just demonstrates the usage of RadioListTile, further info on styling active-/nonactive-Tiles can be found easily.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
/// main application widget
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Application';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const MyStatefulWidget(),
),
);
}
}
/// stateful widget that the main application instantiates
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
enum Fruit { apple, banana }
/// private State class that goes with MyStatefulWidget
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
Fruit? _fruit = Fruit.apple;
#override
Widget build(BuildContext context) {
return Center(
child: Column(
children: <Widget>[
RadioListTile<Fruit>(
title: const Text('Apple'),
value: Fruit.apple,
groupValue: _fruit,
onChanged: (Fruit? value) {
setState(() {
_fruit = value;
});
},
),
RadioListTile<Fruit>(
title: const Text('Banana'),
value: Fruit.banana,
groupValue: _fruit,
onChanged: (Fruit? value) {
setState(() {
_fruit = value;
});
},
),
],
),
);
}
}
Source
https://googleflutter.com/flutter-radiolisttile/

How to dynamically generate widgets in Flutter?

I have a dummy list of data. I want each item on the list to show a different widget when it is tapped, similar to a contacts app. Defining the widget in the onPressed method always returns the same widget. How can I generate each widget without manually creating each one?
void az() {
int c = "A".codeUnitAt(0);
int end = "Z".codeUnitAt(0);
while (c <= end) {
items.add(FlatButton.icon(
icon: Icon(Icons.image_aspect_ratio),
label: Text(
String.fromCharCode(c),
style: TextStyle(fontSize: 20),
),
onPressed: (){
print(String.fromCharCode(c)); //This should return a different widget
},
));
c++;
}
}
You can copy paste run full code below
You can declare a local variable start in for loop
code snippet
void az() {
int c = "A".codeUnitAt(0);
int end = "Z".codeUnitAt(0);
for (int start = c; start <= end; start++) {
items.add(FlatButton.icon(
icon: Icon(Icons.image_aspect_ratio),
label: Text(
String.fromCharCode(start),
style: TextStyle(fontSize: 20),
),
onPressed: () {
print(String.fromCharCode(
start)); //This should return a different widget
},
));
}
}
output
I/flutter (12880): A
I/flutter (12880): B
I/flutter (12880): C
working demo
full code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#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;
List<Widget> items = [];
void az() {
int c = "A".codeUnitAt(0);
int end = "Z".codeUnitAt(0);
for (int start = c; start <= end; start++) {
items.add(FlatButton.icon(
icon: Icon(Icons.image_aspect_ratio),
label: Text(
String.fromCharCode(start),
style: TextStyle(fontSize: 20),
),
onPressed: () {
print(String.fromCharCode(
start)); //This should return a different widget
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
SecondRoute(yourParameter: String.fromCharCode(start))));
},
));
}
}
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
az();
setState(() {});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: items,
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class SecondRoute extends StatelessWidget {
final String yourParameter;
const SecondRoute({Key key, this.yourParameter}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('$yourParameter'),
),
body: Center(
child: RaisedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Go back!'),
),
),
);
}
}

How to pass data by reference to stateful widget? I want to change a variable inside a stateful widget and update original variable

I want to pass data to a stateful widget, change the data inside the widget and also have it updated in the original location.
I want to avoid global variables and I am wondering if I can pass a variable to a stateful widget by reference.
Here is some example code where data is passed to the widget. If I use the slider, the counter is only update inside the widget, not in the main layout tree.
I appreciate any help.
import 'package:flutter/material.dart';
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(
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 inside main layout tree: $_counter',
style: Theme.of(context).textTheme.title,
),
TestWidget(_counter),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class TestWidget extends StatefulWidget {
int counter;
TestWidget(this.counter);
#override
_TestWidgetState createState() => _TestWidgetState();
}
class _TestWidgetState extends State<TestWidget> {
#override
Widget build(BuildContext context) {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
side: BorderSide(color: Colors.orange),
),
child: Column(children: <Widget>[
Text("This card is an external Widget"),
Slider(
min: 0,
max: 100,
divisions: 101,
onChanged: (double val) {
setState(() {
widget.counter = val.toInt();
});
},
value: widget.counter.toDouble(),
),
Text("Counter inside external widget: ${widget.counter}",
style: Theme.of(context).textTheme.title),
]));
}
}
Actually the StatefulWidget is immutable and its state is maintained by the State class. You cannot pass values by reference and update the widgets. Instead you can just pass the value and and a function that updates the value.
Example:
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([int value]) {
setState(() {
_counter = value ?? (_counter + 1);
});
}
#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 inside main layout tree: $_counter',
style: Theme.of(context).textTheme.title,
),
TestWidget(
counter: _counter,
updateCount: _incrementCounter,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class TestWidget extends StatelessWidget {
final int counter;
final ValueChanged<int> updateCount;
const TestWidget({Key key, this.counter, this.updateCount}) : super(key: key);
#override
Widget build(BuildContext context) {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
side: BorderSide(color: Colors.orange),
),
child: Column(
children: <Widget>[
Text("This card is an external Widget"),
Slider(
min: 0,
max: 100,
divisions: 101,
onChanged: (double val) {
updateCount(val.toInt());
},
value: counter.toDouble(),
),
Text(
"Counter inside external widget: $counter",
style: Theme.of(context).textTheme.title,
),
],
),
);
}
}
Hoper that helps!

Flutter: Widget State: Is this code safe?

The code below is an example to illustrate this question. The code below works, however the following line:
class WidgetCustom extends StatefulWidget {
has "WidgetCustom" underlined in green in vsCode, and when the cursor is positioned over it, it shows the message:
"This class (or a class this class inherits from) is marked as #immutable, but one or more of its instance fields are not final".
The code works fine.
Is it safe to use this code?
Is there a way to achieve this without the warning?
import 'package:flutter/material.dart';
class WidgetCustom extends StatefulWidget {
_WidgetCustomState _state;
WidgetCustom({#required int iCount}) {
_state = _WidgetCustomState(iCount);
}
#override
State<StatefulWidget> createState() {
return _state;
}
int get getIcount => _state.iCount;
}
class _WidgetCustomState extends State<WidgetCustom> {
int iCount;
_WidgetCustomState(this.iCount);
#override
Widget build(BuildContext context) {
return Container(
child: Row(children: <Widget>[
Column(
children: <Widget>[
RaisedButton(
child: const Text("Please tap me"),
onPressed: () {
setState(() => iCount = iCount + 1);
}),
SizedBox(height: 40),
Text("Tapped $iCount Times")
],
),
]));
}
}
Edited to add main.dart
import 'package:flutter/material.dart';
import 'widgetCustom.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: 'Custom Widget Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
WidgetCustom _widgetCustom;
String _sMessage = "Fab has not been pressed";
#override
void initState() {
super.initState();
_widgetCustom = WidgetCustom(iCount: 99);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(children: [
_widgetCustom,
SizedBox(height: 40),
Text(_sMessage),
]),
floatingActionButton: FloatingActionButton(
onPressed: _fabPressed,
tooltip: 'Get Value',
child: Icon(Icons.add),
),
);
}
_fabPressed() {
setState(() => _sMessage =
"Value from last button click = ${_widgetCustom.getIcount}");
}
}
Pass the initial value to the constructor when creating the widget as a final value, and then get it from the State class.
Updated code:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData.dark(),
home: MyHomePage(title: 'Custom Widget Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
WidgetCustom _widgetCustom;
String _sMessage = "Fab has not been pressed";
int _value = 99;
#override
void initState() {
super.initState();
_widgetCustom = WidgetCustom(iCount: _value, function: _update);
}
void _update(int value) {
setState(() {
_value = value;
_widgetCustom = WidgetCustom(iCount: _value, function: _update);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.title)),
body: Column(
children: [
_widgetCustom,
SizedBox(height: 40),
Text(_sMessage),
],
),
floatingActionButton: FloatingActionButton(
onPressed: _fabPressed,
tooltip: 'Get Value',
child: Icon(Icons.add),
),
);
}
_fabPressed() {
setState(() => _sMessage = "Value from last button click = ${_value}");
}
}
class WidgetCustom extends StatefulWidget {
final int iCount;
final Function function;
WidgetCustom({#required this.iCount, this.function});
#override
State<StatefulWidget> createState() {
return _WidgetCustomState();
}
}
class _WidgetCustomState extends State<WidgetCustom> {
int _iCount;
#override
void initState() {
super.initState();
_iCount = widget.iCount;
}
#override
Widget build(BuildContext context) {
return Container(
child: Row(
children: <Widget>[
Column(
children: <Widget>[
RaisedButton(child: const Text("Please tap me"), onPressed: (){
_iCount = _iCount + 1;
widget.function(_iCount);
}),
SizedBox(height: 40),
Text("Tapped $_iCount Times")
],
),
],
),
);
}
}