I have a widget called 'HomePage' in my application. I gave a pageview to its body and put 2 pages in it. In the Appbar, there is a title that shows the user's money. on one of the pages there is a button that reduces money. but when I press this button the money is not updated immediately, if I change the page then it is updated. How do I immediately update the money value in the appbar without the page changing?
/////////////////// HOMEPAGE WİDGET ///////////////////
import 'package:flutter/material.dart';
import 'package:flutterapp/demo/user_infos.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(UserInfos.money.toString())),
body: PageView(
children: [
Page1(),
Page2(),
],
),
);
}
}
////////////////////////////////////////////////////////
///// UserInfo class//////
class UserInfos {
static int money = 25000;
...
...
...
...
}
////////////////////////////////////////////////////////////////
//////////// Page1()/////////////////////////////////
class Page1 extends StatefulWidget {
const Page1({super.key});
@override
State<Page1> createState() => _Page1State();
}
class _Page1State extends State<Page1> {
@override
Widget build(BuildContext context) {
return Center(
child: ElevatedButton(
onPressed: () {
setState(() {
UserInfos.money -= 1;
});
},
child: const Text('decrease money')),
);
}
}
I also got help from chatgpt or something, but it didn't work. I need to change the page or do a hot reolad by doing 'ctrl+s'. only then the money value is updated
Calling [setState] notifies the framework that the internal state of this object has changed in a way that might impact the user interface in this subtree, which causes the framework to schedule a [build] for this [State] object.
The key is rebuild your widgets tree everytime you change your widget' states
When you do like this
class Page1 extends StatefulWidget {
UserInfos userInfo;
const Page1({super.key, required this.userInfo});
@override
State<Page1> createState() => _Page1State();
}
class _Page1State extends State<Page1> {
@override
Widget build(BuildContext context) {
return Center(
child: ElevatedButton(
onPressed: () {
setState(() { // Page1 state changed, not homepage state
widget.userInfo.money -= 1;
});
},
child: const Text('decrease money')),
);
}
}
only page1 widget will be scheduled to rebuild. If you want to update changes to your homepage widget, you need to call setState
inside your homepage widget
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
UserInfos _userInfo = new UserInfos();
// setState here when userInfos changed
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(_userInfo.money.toString())),
body: PageView(
children: [
Page1(userInfo: _userInfo),
Page2(userInfo: _userInfo),
],
),
);
}
}
There are many way to do this, the simplest is callback.
Your HomePage widget:
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
void _updateState() {
setState(() {
UserInfo.money += 100;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(UserInfo.money.toString())),
body: PageView(
children: List.generate(
3,
(index) => Page(
action: 'Action $index',
onPressed: _updateState, // Pass your actions to child widget
),
),
),
);
}
}
Your Page widget:
class Page extends StatefulWidget {
const Page({
super.key,
required this.action,
this.onPressed,
});
final String action;
final Function()? onPressed;
@override
State<Page> createState() => _PageState();
}
class _PageState extends State<Page> {
@override
Widget build(BuildContext context) {
return Center(
child: ElevatedButton(
onPressed: widget.onPressed, // Callback to change state
child: Text(widget.action),
),
);
}
}
Your _updateState()
function is called inside HomePage widget, so it will rebuild your widget after state changed.
A better way is make your state observable
class UserInfo {
// when you change money value, every listeners of this will be updated.
static ValueNotifier<int> money = ValueNotifier(25000);
}
Update money value by
UserInfo.money.value = 100; // new value
And make your appbar listen for money value changes
AppBar(
title: ValueListenableBuilder( // Your appbar will listen any UserInfo.money change
valueListenable: UserInfo.money,
builder: (context, value, child) => Text(value.toString()),
),
)
Full solution
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: ValueListenableBuilder( // Your appbar will listen any UserInfo.money change
valueListenable: UserInfo.money,
builder: (context, value, child) => Text(value.toString()),
),
),
body: PageView(
children: List.generate(
3,
(index) => Page(
action: 'Action $index',
),
),
),
);
}
}
class UserInfo {
static ValueNotifier<int> money = ValueNotifier(25000);
}
class Page extends StatefulWidget {
const Page({
super.key,
required this.action,
});
final String action;
@override
State<Page> createState() => _PageState();
}
class _PageState extends State<Page> {
@override
Widget build(BuildContext context) {
return Center(
child: ElevatedButton(
onPressed: () {
UserInfo.money.value += 100;
},
child: Text(widget.action),
),
);
}
}