I used Checkbox to update some data. The sharing data method is Consumer<T>.
The problem is that Checkbox can work ok on changing state, but the checking animation is missing.
I have located the problem, if I used notifyListeners()to notify the Data changed, then the animation of Checkbox is missing.
The Widget code just like below:
bool _value = false;
#override
Widget build(BuildContext context) {
// TODO: implement build
return Row(
children: <Widget>[
Checkbox(
activeColor: Colors.green,
value: _value,
onChanged: (value) {
setState(() {
_value = value;
widget.updateEnergy(); //The problem is Here!!!!!
});
},
),
Text("foodName",
style: TextStyle(color: Colors.blue,
fontSize: 13, fontWeight: FontWeight.w300),
),
Spacer(),
],
);
}
If I used the widget.updateEnergy(); to update the data in onChanged(), the animation is missing.
The updateEnergy() as below:
void updateCurrentEnergy(){
_currentEnergyCount.setValue(_getCurrentEnergyCount(), _dailyTargetEnergy.unit);
notifyListeners();
}
The key is "notifyListeners()", if removed the invoke, the animation is return.
And my data class is "with ChangeNotifier", so notifyListeners() is from ChangeNotifier.
And I used the ChangeNotifierProvider.value(value: dailyRecord), to share the data in the father node. And user the Consumer<DailyRecord>(...using data.
I hope your help, how can get the animation of checkbox.
Thank you!
Related
I'm developing my first app with flutter. At some point I was wondering :Am I developping the UX part correctly ? Meaning am I using the proper widget, is there any better way to do that etc.. I find out about Flutter Performance on Intellj Idea and I saw that most of the pages I developed are red...
FYI : The code I created for a simple page
Flutter inspector result => radio-btn-aligned
import 'package:flutter/material.dart';
import 'package:testapp/my_theme.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: MyAppTheme(ctx: context).defaultTheme,
home: new Scaffold(
appBar: new AppBar(
title: new Text("Title"),
),
body: AddDailyTaskPage()),
);
}
}
enum Options { goal, category }
class AddDailyTaskPage extends StatefulWidget {
#override
_AddDailyTaskPageState createState() => new _AddDailyTaskPageState();
}
class _AddDailyTaskPageState extends State<AddDailyTaskPage> {
Options _options = Options.goal;
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
Container(
child: Text("Task Description"),
),
Container(child: Row(
children: [
Expanded(
child: Row(
children: [
Radio<Options>(
value: Options.goal,
groupValue: _options,
onChanged: (Options value) {
setState(() {
_options = value;
});
},
),
Text(
'Goal',
style: new TextStyle(fontSize: 16.0),
)
]
)
),
Expanded(
child: Row(
children: [
Container(
child: Radio(
value: Options.category,
groupValue: _options,
onChanged: (Options value) {
setState(() {
_options = value;
});
},
),
),
Container(
child: Text(
'Category',
style: new TextStyle(
fontSize: 16.0,
),
),
)
],
)
)
],
),)
// Container(
// child: TextField(
// maxLines: 10,
// decoration: InputDecoration(
// // suffixIcon:
// focusedBorder: OutlineInputBorder(
// borderSide: BorderSide(color: Colors.grey, width: 5.0),
// ),
// enabledBorder: OutlineInputBorder(
// borderSide: BorderSide(color: Colors.black, width: 5.0),
// ),
// hintText: 'Description task',
// ),
// ),
// )
],
),
);
}
}
To see the difference, I checked the sample code provided while creating a flutter Project
FYI :
flutter performance result : auto-increment
As we can see on the previous pic, it doesn't seems optimised.. :/
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(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
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),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
Few questions :
1/ Does anyone has / knows a good flutter repo with ton of examples based on performance ?
2/ What is your standard in term of perf ? I mean on my virtual device, the UX seems fluid but if the Flutter Perf is "red", then I'm guessing I'm doing something wrong and there is a better way to do ?
3/ Do you guys knows a website / forum / someone who's willing to do a core review once a week to help me implementing good pattern in my flutter application ?
4/ What is wrong with my current design ? What is wrong with the default design ? Why the performance are doesn't seems good ? I started to read the official documentation for the perf, but how could I know if the UX itself have good perf or not ? Actually by testing some stuff, I find out that putting everything in Container / Row or Column widget, improved a lot the performance but even with this widgets, it's not enough :/
Any advice ?
Thanks for your help
You use a StatefulWidget for the whole page. That means that when you call setState() the whole page gets rebuild.
One example is:
Radio<Options>(
value: Options.goal,
groupValue: _options,
onChanged: (Options value) {
setState(() {
_options = value;
});
},
),
There's no need to rebuild your whole page when you set an option. If you extract that code out into it's own StatefulWidget, only this portion gets rebuild.
You can click on "Track Widget rebuilds" to see what Widgets do rebuild in your app and then think about whether those Widget actually should rebuild.
Once you do smaller Widgets you get the problem that events in one Widget are supposed to influence other Widgets. That's when state management solutions come into play. The weather example of the Bloc package shows a good structure of an app that you can copy.
I am making an application and for showing circular progress indicator i had used library of modal_progress_hud and when i set the state of inAsyncCall true or false, it rebuild all of its child widgets inside modal_progress_hud. And i do not want to rebuild all the UI again and again. It decreases the efficency of application by increasing usage of GPU. Is there any other way to make it better, that we just change the property and rebuild that property of widget only instead of whole screen and widgets inside it.
bool circularindicator = false;
Color circularColor;
double circularOpacity;
child: ModalProgressHUD(
inAsyncCall: circularindicator,
child: body,
color: circularColor,
opacity: circularOpacity,
),
In body, we have all of the widgets in listview.
And after pressing the login button we called the showProgress() method and set the state and rebuild the widget.
#override
showProgress() {
setState(() {
circularindicator = true;
circularOpacity = 0.5;
circularColor = Colors.grey;
});
}
Make your class StatelessWidget and use Provider Package for state management. Inside the widget build function keep the Provider listener false and wrap the changeable widget with Consumer Widget.
Widget build(BuildContext context) {
final cart = Provider.of<Cart>(context, listen: false);
return GridTileBar(
leading: Consumer<Product>(
builder: (ctx, product, child) => IconButton(
onPressed: () {
product.toggleFavoriteStatus();
},
icon: Icon(product.isFavorite ? Icons.favorite : Icons.favorite_border),
color: Theme.of(context).accentColor,
),
),
trailing: IconButton(
icon: Icon(Icons.shopping_cart),
onPressed: () {
cart.addItem(product.id, product.title, product.price);
},
color: Theme.of(context).accentColor,
),
backgroundColor: Colors.black87,
title: Text(product.title),
);
}
For more details on provider package: State Management Using Provider
I saw linear degradation of framerate UI when I launch speed_dial animation plugin. The problem appear when I add sharedpref function here:
#override
Widget build(BuildContext context) {
sharedpref_function();
return Scaffold(
to listen a saved value, even If the sharedpref is empty I have this degradation.
After 10min whithout doing nothing before, I measure 1120ms/frame when I call _renderSpeedDial
Here is the full code :
bool _dialVisible = true;
Color _speedDial = Colors.pink;
sharedpref_function() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
}
);
}
_renderSpeedDial() {
return SpeedDial(
animatedIcon: AnimatedIcons.add_event,
animatedIconTheme: IconThemeData(size: 22.0),
backgroundColor: _speedDial,
// child: Icon(Icons.add),
/* onOpen: () => print('OPENING DIAL'),
onClose: () => print('DIAL CLOSED'),*/
visible: _dialVisible,
curve: Curves.bounceIn,
children: [
SpeedDialChild(
child: Icon(Icons.fullscreen_exit, color: Colors.white),
backgroundColor: Color(0xffa088df),
onTap: () {
setState(() {
});
},
label: '1',
labelStyle: TextStyle(fontWeight: FontWeight.w500,color: Colors.white),
labelBackgroundColor:Color(0xffa088df),
),
],
);
}
#override
Widget build(BuildContext context) {
sharedpref_function(); // here the sharedpref I use to listen saved value
return Scaffold(
body: Stack(
children: <Widget>[
Padding
(
padding: const EdgeInsets.only(right:10.0, bottom:10.0),
child:
_renderSpeedDial(),
),
],
)
);
}
}
Your sharedpref_function() method is being called inside your build method. That's not recommended at all because it will be called on every frame the UI needs to be rebuild and your code, having an animation there, will be called at 60fps (on every frame).
Move your method inside initState or didChangeDependencies (there're even more methods that get called once or a few times like didChangeDependencies).
When you need to update values, you could do it inside an onTap gesture and that's it.
Also, test your app in --release (release mode) to truly test the speed of your app.
I'm new to flutter and experimenting with Sateful widget. Here is the thing, In my UI screen layout, I have two different widgets
DropdownButton widget
TextFormField which holds card number.
When I was trying to update dropdown selected value to the DropdownButton widget, it automatically clears the text in TextFormField. Does it require to store text in global variable to restore again every time we call setState() method to update the values?
Here is the code for widgets,
DropdownButton
new Padding(
padding: const EdgeInsets.all(15.0),
child: new Column(
children: <Widget>[
DropdownButton<String>(
value: _referPractice,
isDense: true,
hint: new Text(CONST_SELECT),
items: _stdCodesList.map((value) {
return new DropdownMenuItem<String>(
value: value.dialCode,
child: new Text("${value.code} ${value.dialCode}"),
);
}).toList(),
onChanged: (String newValue) {
setState(() {
_referPractice = newValue; // here I`m trying to update selected value.
});
},
)
],
)),
TextFormField
TextFormField(
controller: _textController,
keyboardType: TextInputType.number,
style: new TextStyle(
fontWeight: FontWeight.w200,
color: Colors.black,
fontSize: 18.0,
),
decoration: new InputDecoration(
hintText: HING_ENTER_NUMBER,
suffixIcon: CircleIconButton(
onPressed: () {
this.setState(() {
_textController.clear();
});
},
)),
maxLines: 1,
validator: (value) {
if (value.isEmpty) {
return ERROR_CARD_DETAILS;
}
},
),
I understand that Stateful widget rebuild the widget every time when ever it calls setState but how do I persist the data for form data which is not stored anywhere yet.
Suggestions please! Thanks in advance.
With the given code, one mistake I can think of is creating TextEditingController every time.
#override
Widget build(BuildContext context) {
var _textController = TextEditingController(); //problem
// build and return widgets
It should be in outside of build method. We can have it in constructor or initState.
If you have _textController outside build, can you add some more code?
new CircleAvatar(
backgroundColor: Colors.black87,
backgroundImage: new NetworkImage(url),
radius: 45.0,
)
I Want to show a local image in CircleAvatar until the NetworkImage fully loads from the internet.
You may want to try a FadeInImage wrapped in a ClipOval. FadeInImage provides a placeholder property you can use while the network image is loading.
Note: ClipOval can be expensive if you do it a lot, so use it sparingly.
There is a new official widget for this now!
First, create a folder called assets in the project root directory.
Then, mention the folder in pubspec.yaml file (also found in the project root directory):
flutter:
uses-material-design: true
assets:
- assets/
You can put a picture there, for example, put this as ./assets/loading.gif.
(If you changed files in assets folder, hot reload won't work. Make sure you restart the app entirely.)
Now you can refer to the loading file in the code:
FadeInImage.assetNetwork(
placeholder: 'assets/loading.gif',
image: 'https://github.com/flutter/website/blob/master/src/_includes/code/layout/lakes/images/lake.jpg?raw=true',
);
For more details: https://flutter.io/docs/cookbook/images/fading-in-images#from-asset-bundle
Use a StateflWidget and you can add a listener to the ImageStream and override the initState to trigger a replacement between the local image and the one obtained from the internet when it is fully loaded.
I have used a high resolution image to show the loading time:
var _loadImage = new AssetImage(
'assets/img/basic2-090_loader_loading-512.png');
var _myEarth = new NetworkImage(
"http://qige87.com/data/out/73/wp-image-144183272.png");
bool _checkLoaded = true;
#override
void initState() {
_myEarth.resolve(new ImageConfiguration()).addListener((_, __) {
if (mounted) {
setState(() {
_checkLoaded = false;
});
}
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(child: new Container(
decoration: new BoxDecoration(shape: BoxShape.circle,),
height: 80.0,
width: 80.0,
child: new CircleAvatar(
backgroundColor: Theme
.of(context)
.scaffoldBackgroundColor,
backgroundImage: _checkLoaded ? _loadImage : _myEarth,
),)
)
);
}
}
Two way to solve your problem
1) Using Image.network : If you want to show progressbar,simmer or any other widget when image loading.
Image.network(
"URL",
fit: BoxFit.cover,
loadingBuilder: (BuildContext ctx, Widget child, ImageChunkEvent loadingProgress) {
if (loadingProgress == null) {
return child;
}else {
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.green),
),
);
}
},
)
2) Using FadeInImage : If you want to display your local image when network image loading
FadeInImage.assetNetwork(
image:"URL",
placeholder:"assets/loading.png" // your assets image path
fit: BoxFit.cover,
)
You can also use the frameBuilder property. The good thing: you can implement your custom placeholder widget here.
Image.network('https://example.com/my-image',
height: 100,
frameBuilder: (context, child, frame, _) {
if (frame == null) {
// fallback to placeholder
return MyPlaceholderWidget();
}
return child;
}
)
While large images load, show a fallback asset!
new PlutoImage.networkWithPlaceholder("http://68.media.tumblr.com/f7e2e01128ca8eb2b9436aa3eb2a0a33/tumblr_ogwlnpSpcU1sikc68o1_1280.png", new Image.asset("assets/placeholder.png"));
https://github.com/FaisalAbid/pluto
You can Use FadeInImage.
Use a placeholder from asset
FadeInImage.assetNetwork(
placeholder: "assets/images/image1.png",
image:"URL"
),
Use a placeholder from memory
FadeInImage.memoryNetwork(
placeholder: localImageBytes,
image:"URL"
),
I developed a package named flutter_url_image_load_fail to define the loading and failed to load widgets:
LoadImageFromUrl(
'https://upload.wikimedia.org/wikipedia/commons/1/17/Google-flutter-logo.png', //Image URL to load
(image) => image, //What widget returns when the image is loaded successfully
() => Text('Loading...'), //What widget returns when the image is loading
(IRetryLoadImage retryLoadImage, code , message){ //What widget returns when the image failed to load
return RaisedButton(
child: Text('Try Again'),
onPressed: (){
retryLoadImage.retryLoadImage(); //Call this method to retry load the image when it failed to load
},
);
},
requestTimeout: Duration(seconds: 5) //Optionally set the timeout
)
There is a new cached_network_image package that has a "loading" and an "error" images. Along with auto image caching.
https://stackoverflow.com/a/57493334/5502121
You can set as a placeholder anything you want, for example from your assets use Image.asset('assets/images/my_placeholder.png')