Why Stateless widget rebuild on refresh in flutter - flutter

I have created a demo,
I have made a stateful home screen where a column containing two containers.
one is stateless made separately...
On scaffold I have placed refresh button to change randomly color but I don't want to make changes to stateless widget , but it changes on refresh..
To make my point clear I have made this demo, actually I am stuck in my app
here is my code
class HomeScreen extends StatefulWidget {
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Color.fromRGBO(Random().nextInt(255), Random().nextInt(255), Random().nextInt(255), 1),
title: Text('Demo'),
actions: [IconButton(onPressed: (){
setState(() {
});
}, icon: Icon(Icons.refresh))],
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
height: 100,
color: Color.fromRGBO(Random().nextInt(255), Random().nextInt(255), Random().nextInt(255), 1),
),
MyWidget(),
],
)
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 100,
color: Color.fromRGBO(Random().nextInt(255), Random().nextInt(255), Random().nextInt(255), 1),
child: Center(child: Text('Why this ,stateless ,also changed on refresh..')),
);
}
}

Use const with key constructor
class MyWidget extends StatelessWidget {
const MyWidget({Key? key}) : super(key: key);
And use
const MyWidget(),

The mywidget eventhough is a stateless widget its placed inside a stateful widget. So each time the stateful widget ia built it will rebuild the stateless widget too

Related

How to constraint item position in flutter?

I try to make a list of widget. It look like this:
I know of no such thing as Constraint Layout in flutter. But I need something to position my arrow icon in a fixed position on the right. To put it simple, this is my widget code:
Row(
children:[
SizedBox(),
Column(),//this is all the item on the left
Spacer(),
Expanded(// this is the heart and arrow button
child: Column()
)
]
)
I notice that if my column on the left get too wide, my arrow and heart icon is shifted out of line.
How to put my icon in fixed position to the right?
here try this, You have to wrap middle column with expanded so it will take the maximum space available
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const Center(
child: MyStatefulWidget(),
),
),
);
}
}
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
bool isChecked = false;
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: 10,
itemBuilder: (_, index) => Container(
padding: EdgeInsets.all(15),
margin: EdgeInsets.symmetric(vertical: 5),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),border: Border.all(width: 1.5)),
child: Row(
children: [
Container(
width: 25,
height: 40,
color: Colors.black,
),
Expanded(
child: Column(children: [
//put your children here
]),
),
//this will be always on right
Column(
children: [
Icon(Icons.heart_broken),
Icon(Icons.chevron_right),
],
)
],
),
),
);
}
}

Flutter: Prevent `build` being called when `Refresher` is used

I am using the pull_to_refresh package.
I am having a Stack() with two elements. One of them is the Refresher(). When I pull down on my screen, activating the refreshing animation, the build method is called constantly. The problem is that my second Widget in my Stack is quite complex to build and takes some time. I want to prevent having it build all the time when triggering the Refresher-Animation. Is this possible?
My simplified code would look like this:
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
body: Stack(children: <Widget>[
SafeArea(
child: Column(children: [
Expanded(
child: Container(
margin: EdgeInsets.all(0),
width: 100.w,
constraints: const BoxConstraints.expand(),
child: SizedBox(
width: 100.w,
child: Refresher( refresher stuff )
)
)
)
)
),
SecondItem()
)
)
}
Somehow the build method of SecondItem is called all the time. Not the build method of the whole scaffold.
If Your second Item dont want to get refresh, then add it as a separeate class like,
Expanded(
child: Container(
margin: EdgeInsets.all(0),
width: 100.w,
constraints: const BoxConstraints.expand(),
child: SizedBox(
width: 100.w,
child: Refresher( refresher stuff )
)
)
)
)
),
SecondItem()
)
class SecondItem extends StatefulWidget {
#override
_SecondItemState createState() => _SecondItemState();
}
class _SecondItemState extends State<SecondItem> {
int counter = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Text("Your Second Widget"),
);
}
}
Now your SecondItem() will not get refresh when you refresh your FirstItem()
Since I wasn't really able to replicate the problem, I build a working structure that implements refreshing.
First the main widget, in my case MyHomePage.
This widget implements the Scaffold and Stack with FirstWidget and SecondWidget as children.
class MyHomePage extends StatefulWidget {
const MyHomePage({
Key? key,
}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: const <Widget>[
FirstWidget(),
SecondWidget(),
],
),
);
}
}
FirstWidget is a statefull widget with a counter in the state.
It implements the refresher with a specific controller.
Once the refresh is triggered, it calls set state and updates the counter within his state.
That should trigger only his build again and not any other.
I implemented a Text to show the counter value increasing at each refresh, and a Print to expose the build.
class FirstWidget extends StatefulWidget {
const FirstWidget({
Key? key,
}) : super(key: key);
#override
State<FirstWidget> createState() => _FirstWidgetState();
}
class _FirstWidgetState extends State<FirstWidget> {
late int _counter;
late RefreshController _refreshController;
#override
void initState() {
_counter = 1;
_refreshController = RefreshController(initialRefresh: false);
super.initState();
}
#override
Widget build(BuildContext context) {
print('First widget built');
return SafeArea(
child: Column(
children: [
Container(
margin: const EdgeInsets.all(0),
width: double.infinity,
height: 500,
color: Colors.red,
child: SmartRefresher(
controller: _refreshController,
onRefresh: () async {
setState(() {
_counter++;
});
await Future.delayed(const Duration(milliseconds: 1000));
_refreshController.refreshCompleted();
},
),
),
Text("Counter: $_counter"),
],
),
);
}
}
Last we got the SecondWidget which is a another simple widget with a print statement.
In case of build it writes on the console.
When the FirstWidget refresh, the second doesn't build becouse his state has not changed.
class SecondWidget extends StatefulWidget {
const SecondWidget({
Key? key,
}) : super(key: key);
#override
State<SecondWidget> createState() => _SecondWidgetState();
}
class _SecondWidgetState extends State<SecondWidget> {
#override
Widget build(BuildContext context) {
print('Second widget built');
return const Center(child: Text('Second here!'));
}
}
Possible cause of your problem.
It could be that when refreshing, you actually are updating the state of a parent widget that, on cascade, causes the re build of your second widget.
If state is handled correctly, and your second widget doesn't depends on your first widget state, the refresh should not rebuild the second.

How to change variable of a stateful widget from another widget?

How to change a variable of a Widget from another widget?
This is the main stateful widget called HomePage:
class _HomePageState extends State<HomePage> {
#override
num counter = 0;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Title")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [Text(counter.toString()), CardWidget()],
),
));
}
}
This is CardWidget which is added to HomePage:
class CardWidget extends StatefulWidget {
const CardWidget({Key? key}) : super(key: key);
#override
_CardWidgetState createState() => _CardWidgetState();
}
class _CardWidgetState extends State<CardWidget> {
#override
Widget build(BuildContext context) {
return Card(
child: Column(
children: [
Text("Press the button to increment the counter"),
ElevatedButton(
onPressed: () {
//Something here to increment the counter in HomePage
},
child: const Text('Increment'),
),
],
));
}
}
This is what is shown on the screen:
Is it possible to create a connection between the two widgets: if I tap the button something happens in the HomePage Widget? (similar to delegate in UIKit)
You can pass Function parameter.
In your CardWidget add Function parameter.
class CardWidget extends StatefulWidget {
//Add clicked function
final Function onClicked;
const CardWidget({Key? key, required this.onClicked}) : super(key: key);
#override
_CardWidgetState createState() => _CardWidgetState();
}
class _CardWidgetState extends State<CardWidget> {
int _count = 0;
#override
Widget build(BuildContext context) {
return Card(
child: Column(
children: [
Text("Press the button to increment the counter"),
ElevatedButton(
onPressed: () {
//Something here to increment the counter in HomePage
//Execute `onClicked` and pass parameter you want
_count++;
widget.onClicked(_count);
},
child: const Text('Increment'),
),
],
));
}
}
then on HomePage add onClicked parameter
class _HomePageState extends State<HomePage> {
#override
num counter = 0;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Fontanelle")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(counter.toString()),
CardWidget(
//Add onClicked
onClicked:(count){
print("Clicked "+count.toString());
}
)
],
),
));
}
}
You have some solution for this case:
1, Create GlobalKey for StatefullWidget, and you can access to State from HomePage
2, Create a Stream from Homepage and pass to StatefullWidget
3, Pass param to StatefullWidget and use didUpdateWidget on state to listen.
I remember that was my first question when did my first step in flutter, how say #dangngocduc is true but my advice is that read about BLOC
https://pub.dev/packages/flutter_bloc
This is very helpful to do this.

How can I pass flutter textediting controller value from one stateless widget to another stateless widget

Here Is the main.dart file:
import 'package:flutter/material.dart';
import 'package:untitled/button.dart';
import 'package:untitled/form.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, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[MyForm(), MyButton()],
),
),
);
}
}
Here is form.dart file:
import 'package:flutter/material.dart';
class MyForm extends StatelessWidget {
#override
Widget build(BuildContext context) {
TextEditingController x = TextEditingController();
return Container(
child: Center(
child: TextField(
controller: x,
decoration: InputDecoration(labelText: "Name"),
),
),
);
}
}
Here is button.dart file:
import 'package:flutter/material.dart';
class MyButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: ElevatedButton(
onPressed: () {
//if I press this button the textediting field will show 10
},
child: Text("Pass 10"),
),
),
);
}
}
How can I show data in text editingfield if I press the button in button.dart?
What is the optimal way to do these things?
Should I use statefulwidgets in statelesswidget or statelesswidgets in statefulwidgets?
My thought is to use redux or provider to pass this kind of things. Although I don't know this simple thing need state management system or not.
You need to lift the state up (create your controller in the main and pass it to the children)
// main.dart
class _MyHomePageState extends State<MyHomePage> {
TextEditingController x = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[MyForm(x), MyButton(x)],
),
),
);
}
}
class MyForm extends StatelessWidget {
MyForm(this.x);
final TextEditingController x;
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: TextField(
controller: x,
decoration: InputDecoration(labelText: "Name"),
),
),
);
}
}
class MyButton extends StatelessWidget {
MyButton(this.x);
final TextEditingController x;
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: ElevatedButton(
onPressed: () {
x...
//if I press this button the textediting field will show 10
},
child: Text("Pass 10"),
),
),
);
}
}

Image not showing in Flutter Splash Screen page

My Image.asset file doesn't display my image but other widgets display text, card, etc
assets:
- images/internet_image.jpg
class SplashScreen extends StatefulWidget{
#override
_SplashScreenState createState() =>
_SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen>{
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Stack(
children: <Widget>[
Column(
children: <Widget>[
Image.asset(
'images/internet_image.jpg',
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 1/4,
fit: BoxFit.fitWidth,
),
],
),
],
),
);
}
}
assets: - images/downloaded_image.jpg
and the referenced asset should be the same:
Image.asset('images/downloaded_image.jpg'),
In your code you refer to an asset named images/internet_image.jpg
An extra layer of widget might look like the code below so that the Scaffold doesn't have to field the context call.
class SplashScreen extends StatefulWidget{
#override
_SplashScreenState createState() =>
_SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen>{
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SplashScreenStack(),
);
}
}
class SplashScreenStack extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Column(
children: <Widget>[
Image.asset(
'images/internet_image.jpg',
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 1/4,
fit: BoxFit.fitWidth,
),
],
),
],
);
}
}
Thanks a lot every one for answering. All solutions provided would definitely fix the issue, I decided to upload my splash screen the normal way via pubspec.yaml rather than trying to create a splash screen page.