Problem updating UI with RiverPod in Flutter? - riverpod

I have a Widget containing a button which increases product quantity in a product list and shows the updated value in a textBox. I am using Riverpod for Statemanagement.
Here is a ItemCounter Widget:
final appNotifier = ref.watch(applicationProvider.notifier);
ItemCounter(
quantity: appNotifier.getQuantityByProductId(product.id!),
onChanged: (i) => appNotifier.setProductQuantity(
i: i, productId: product.id!),
),
When I use above code the UI doesn't update after each click on the button! But when I add this line it works, even though I don't use appProvider anywhere in the code!
// appProvider has no usage in the code but it helps to update UI
final appProvider = ref.watch(applicationProvider); <-----This line
final appNotifier = ref.watch(applicationProvider.notifier);
ItemCounter(
quantity: appNotifier.getQuantityByProductId(product.id!),
onChanged: (i) => appNotifier.setProductQuantity(
i: i, productId: product.id!),
),

By design :)
-> When we use ref.watch(applicationProvider), we listen for state changes (and access it) and trigger a rebuild of the widget whenever it has changed.
-> When we use ref.watch(applicationProvider.notifier), we access the listener class and always have the current version of notifier. The rebuilding will not be triggered.

Related

How to boost flutter performance?

How to boost flutter performance and speed up our application.
As I am working on a larg application and I worried about performance issue, becuase I need a supper fast app.
Here I have a large list of items like a chat application and there is something mentioned below that I am worry about:
1: I need scrolling more than 3000 items smoothly
2: I need to update specific item during chat, like: seen, delived, sent or deleted.
3: and navigation
In react-native there is a lot of way like using PureComponent
or shouldComponentUpdate(nextProps, nextState) function to decrease rendering.
for example in react-native we can split a big list Component into sub Component of items, then we can use shouldComponentUpdate(nextProps, nextState) and prevent any rendering does not depends to the current Item.
This aprroach help us to just render the item we need to update.
and also there is alot of other way we can use for speed up our application.
although One big problem I encounted in react-native is, if we scroll very fast more than 300 items there a blank space appears instaed of list items.
So my question is how to find same tips in FLUTTER, I mean Is there a tips/trick for flutter like I mentioned above in react-native? if yes please help me with a simple examples.
Thank you in befor
Here I have a large list of items like a chat application and there is something mentioned below that I am worry about: 1: I need scrolling more than 3000 items smoothly 2: I need to update specific item during chat, like: seen, delived, sent or deleted. 3: and navigation
Use ListView.builder() widget to create smooth scrollable list without any length without any performance issue.
It's functionality basically corresponds to RecyclerView (Native Android).
The items which are not visible on the screen are not rendered so it doesn't practically matter how many items exist in the list in total.
Flutter handles all those minor things for you so that you can focus on other important aspects of your app. Flutter and RN are similar to some extent but they are not the same.
Working Example:
import 'package:flutter/material.dart';
List<String> litems = ["1","2","Third","4"];
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext ctxt) {
return new MaterialApp(
home: new ListDisplay(),
);
}
}
class ListDisplay extends StatelessWidget {
#override
Widget build (BuildContext ctxt) {
return new Scaffold(
appBar: new AppBar(title: new Text("Dynamic Demo"),),
body: new ListView.builder(
itemCount: litems.length,
itemBuilder: (BuildContext ctxt, int index) {
return new Text(litems[index]);
}
)
),
);
}
}
If you still face the same issue, then try running the app in release mode. That should surely solve your issue for the above example.
If you want some more tips on increasing your app's overall performance head over to the links given below,
https://flutter.dev/docs/testing/best-practices
https://medium.com/flutter-community/improve-your-flutter-app-performance-split-your-widgets-935f97e93f7d
To run your app in release mode execute flutter run --release in your app's terminal to run your app in release mode.
While using the provider for example if have data that you will need only on a specific page and this data will be temporarily viewed avoid adding it to the provider I highly advise you to use multiple providers. so you avoid rebuiding all the screen components when changing the state. also if you have too many stateless widgets in a screen and you have only few widgets that are stateful. you should wrap those with state changeable components (stateful) with consumer so when notifying the UI of a state change only those concerne will be updated. that will help avoiding rebuid entire screen.
Use Stateless widgets instead of Stateful widgets in the cases that you dont have to use it so you dont have always to rebuild your application , and try to not Use setState() methode because it is heavy and expensive and use instead of it StreamBuilder()
Split Larger Widgets into smaller widgets
Use Const keyword , Using the const keyword wherever applicable can be a great way to reduce memory usage , and you can use the Const KeyWord in lot of places like EdgeInsets, Color and Text , and Also in Constructors
const EdgeInsets.fromLTRB(16, 4, 16, 8); const Color lightGray = Color(0xFFFEFEFE); const Text('This is a static text')
Also in the constructors:
Column(children: [
const JDSearchIcon(),
InkWell(....),
const JDSearchIcon(),],),
Render only widgets that are visible on the screen , When you are dealing with a large vertical or horizontal list of widgets, you should avoid using a solution that creates all the widgets visible on the screen at once. Instead, try using ListView.builder to improve the performance of your flutter app.
ListView.builder(
itemCount: dataSource.length,
itemBuilder: (context, index) {
return Tweet(dataSource[index]);
},
)

(Flutter) setState of First Page from Second Page

I have a ListView Builder in the first Page, which receives content(items) from the Second Page. When I hit submit button in Second Page, I use:
Navigator.of(context).pop
Which takes me back to First Screen.
The problem is that it is not updating the item created in the list unless i restart the app.
I have tried:
1) Calling an instance of First Page and using:
firstPageinstance.setState((){});
2) I have also tried to call a function from the First Page class, which invokes setState:
firstPageinstance.funtionWhichCallsetStateinFirstageClass();
But none of this works. The list is updated only when I Restart the App.
P.S. The list items are saved and called from an SQLite database. Also, Let me know if you need more details.
When navigating to your SecondPage await the result like this:
bool isUpdated = await Navigator.push(context, MaterialPageRoute(builder: (context) => SecondPage()),)
if(isUpdated) // do your list update logic here
When going back to FirstPage(tap on submit button), send the expected value such as:
Navigator.pop(context, true);
Helpful read: https://flutter.dev/docs/cookbook/navigation/returning-data

How to open a Flutter dialog no matter where the user is within the app

I'm working on a Flutter project where I need the ability to show the user a dialog no matter where they happen to be within the app. Currently I'm executing the showDialog() function in the root level widget that main() kicks off.
That widget implements WidgetsBindingObserver so that I can listen for when the app moves from the background to the foreground via the didChangeAppLifecycleState() function. Anytime this happens I make a request to a service provider and depending on the results I need to show a dialog.
The users will be navigated to some other route anytime this happens, and that's where I seem to be running into trouble. Below is the stripped down function that performs the API call and subsequent showDialog() attempt. But nothing ever happens. I tried wrapping it in a 2 second timer thinking maybe it was an issue of the app just coming back into the foreground, but that didn't make a difference.
void _fetchSuperAwesomeStuff() {
final apiCaller = new SuperAwesomeStuffAPI();
apiCaller.fetchSuperAwesomeStuff().then((List<SuperAwesomeStuff> superAwesomeStuffs) {
if (superAwesomeStuffs != null && superAwesomeStuffs.length > 0) {
SuperAwesomeStuff superAwesomeStuff = superAwesomeStuffs[0];
// .... DOING STUFF WITH THIS SUPER AWESOME STUFF .... //
// NEED TO SHOW A DIALOG.
showDialog(
context: context,
builder: (_) => new AlertDialog(
title: Text('Test Title'),
actions: <Widget>[
new FlatButton(
onPressed: () {
Navigator.pop(context);
},
child: new Text('Close'),
),
],
),
);
}
});
}
Any help with this would be greatly appreciated, thank you!
You need to pass a BuildContext for the context variable, and that context needs to be mounted (the corresponding element) in the tree when you make the call.
Alternatively, you could send a message/stream from your superawesome logic to any part of the app that has a context and listens to the stream. From here you could call the dialog.

Flutter First app

I am learning the Flutter framework and want to ask about the first example app from google tutorials. Namely, I wonder how this code really works.
Widget _buildSuggestions() {
return new ListView.builder(
padding: new EdgeInsets.all(6.0),
itemBuilder: (_, int i) {
if (i.isOdd)
return new Divider(
height: 1.0,
);
final int index = i ~/ 2;
print(index);
print('sug leng ${_suggestions.length}');
if (index >= _suggestions.length) {
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
});
}
How the words are really generated. Like when I inspect the index and the length of _suggestions it's saying that I've generated 20 words but the index is 14 when I slide down the index is moving up and at some point, the new words are added. How it detects when to add and when to stop counting the index? If someone can briefly explain to me this I would appreciate!! Thanks!
First off, ListView's itemBuilder is called lazily. Which means that ListView only request the widgets it needs.
So when you scroll down, the index requested increases.
Then, the widget that instantiate ListView possess a _suggestions. Which is a list of suggestions.
The thing is, when ListView will try to access an index that is not available inside _suggestions, that code detect it and then insert another 10 suggestions to _suggestions before returning it.
While this code works ; don't use this logic if you want to have an infinite scroll behavior. This only work because it displays mocked data. But it won't work with datas coming from a server.
A more production ready approach would be to pass listview a ScrollController. And then listen that scrollcontroller to know when we reach the end of the current content.

What are the options of collapsable list widgets in Flutter?

I need to have some lists collapse by their topics in my project and am wondering if I'll need to implement this from zero or rather use a component from flutter. Does this component exist?
Thanks in advance :)
The Flutter Gallery has two examples that may be relevant for your accordion-like lists.
Expansion Panel Demo & Two-level List Demo
The Expansion Panel demo is probably what you want. If so, take a look at how the demo leverages ExpansionPanel and uses a headerBuilder and body. You can extends this to make the header and bodies as complex as you need. The Gallery demo adds a DemoItem helper class. You can use this pattern or come up with your own design.
Here is a snippet that shows the demo using ExpansionPanelList by passing a callback and the list of DemoItems:
child: new ExpansionPanelList(
expansionCallback: (int index, bool isExpanded) {
setState(() {
_demoItems[index].isExpanded = !isExpanded;
});
},
children: _demoItems.map((DemoItem<dynamic> item) {
return new ExpansionPanel(
isExpanded: item.isExpanded,
headerBuilder: item.headerBuilder,
body: item.build()
);
}).toList()
),

Resources