Flutter page comes too slow. when it contain long data - flutter

My first page contains an flat button. when I press on that button, navigate to another page(MyApp) too slow(It contains long data). How to resolve it.Here is my code.
class MyApp extends StatefulWidget {
final List<String> items;
const MyApp({ this.items});
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
final ScrollController scrollcontroller=ScrollController();
var scaffoldkey=GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
const title = 'Long List';
return Scaffold(
appBar: AppBar(
title: const Text(title),
),
body:Stack(children: [Column(children:[ListView(shrinkWrap:true,children:[ Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Center(
child: Padding(
padding: const EdgeInsets.symmetric(vertical:0.0),
child: Container(
width: 300,
height: 70,
decoration: BoxDecoration(border:Border.all(color:Colors.brown[100],),color: Colors.orange[50], borderRadius:BorderRadius.all(Radius.circular(5.0))),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children:<Widget> [
Container(height: 35,
decoration: BoxDecoration(color: Colors.white,
border: Border.all(color: Colors.brown[300],width: 1),
borderRadius: BorderRadius.circular(5),),),],),),),),],)
]),Expanded(child:ListView(shrinkWrap:true,children:[ListView.builder(
shrinkWrap: true, physics: ScrollPhysics(), scrollDirection: Axis.vertical, itemCount:widget.items.length,
itemBuilder: (context, index) {
return ListTile(title: Text(widget.items[index]),);},),]))])]) );}} ```

The best way is probably to load parts of your data as the user scrolls.
final ScrollController scrollcontroller=ScrollController();
List<String> items;
int numberOfItemsLoadedPerScroll = 15;
List<String> loadedItems = items.sublist(0, 15);
scrollcontroller.addListener(() {
setState(() {
loadedItems = items.sublist(0, loadedItems.Legth+numberOfItemsLoadedPerScroll);
});
});
It would be better to use a Bloc system and update the Stream instead of calling setState. But I think something like this should work.

The list view you are using is extremely inefficient and it takes some amount of time to build and render all the widgets inside it. I would suggest for you to use a list view builder instead as it would render your view way faster and it is the optimized way to have a list view in the first place. https://flutter.dev/docs/cookbook/lists/long-lists
Here are the docs if you want to check them out and it goes without saying that it is a best practice to use a list view builder instead of a regular old list view!
**Edit: for further explanation the list view builder renders items as you scroll down or up the list(15 items required to fill the screen for example) while the list view renders the entirety of the list all at once which may cause the app to be quiet slow and sometimes render it unresponsive by the managing OS (ios or android) if it takes too long.

Related

PageView, inside ListView, inside Column: `Null check operator used on a null value`

I have a PageView, inside a ListView, inside a Column. It seems necessary because of the layout (top green widget should be scrollable, but bottom widget shouldn't be).
class MyWidget extends StatefulWidget {
Widget createPage(int page, Color color) {
return Container(
color: color,
child: Padding(
padding: EdgeInsets.symmetric(vertical: 200.0),
child: Text("Page: $page. Swipe right to go to next page"),
),
);
}
late final List<Widget> pages = [
createPage(1, Colors.purple),
createPage(2, Colors.red)
];
#override
createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
late final PageController _pageController;
int page = 0;
#override
initState() {
super.initState();
_pageController = PageController();
}
#override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: ListView(
children: [
Container(
color: Colors.green,
child: const Text("Top of screen, scrollable.")),
// This works
Column(
children: widget.pages,
),
// PageView tries to take the full height. Without Flexible, PageView is unbounded.
// Flexible(
// child: PageView.builder(
// itemCount: widget.pages.length,
// controller: _pageController,
// onPageChanged: (int index) => setState(() => page = index),
// itemBuilder: (context, index) => widget.pages[index],
// ),
// )
],
),
),
Container(
color: Colors.green, child: const Text("Bottom of screen, always")),
],
);
}
}
However, I get various error based on what I try. This is an unhelpful error, since ListView doesn't have a problem when it's children is a Column:
The following _CastError was thrown during performLayout():
Null check operator used on a null value
The relevant error-causing widget was:
ListView ListView:file:///path/to/project/lib/main.dart:63:18
Code
I've created a Dartpad pad to reproduce this. I've commented out the PageView and replaced it with a column so you can see the app. The issue is replacing Column with 2 pages to PageView.
I've tried various things:
Wrapping ListView in things:
SizedBox.expand(child: ListView(...))
Expanded(child: ListView(...)) following this answer
Flexible(child: ListView(...))
ListView.builder(shrinkWrap: true,...),
Changing mainAxisSize of Column

Flutter TabBar-like Indicator

I need help identifying a Flutter widget that could do things as demonstrated by this GIF:
I am interested in the circular indicator that allows me to browse through the resort locations like above. So far the closest thing that I have found was TabBar, but that isn't what I am exactly looking for. It seems far too complex to modify it to do the things that the GIF does, and I am very certain that there is a simpler solution, but after hours of exhaustive search through the list of widgets, I am not sure what to look for.
Could someone please help me?
Based on your UI, you can use dots_indicator or better smooth_page_indicator
class TestSnippet extends StatefulWidget {
const TestSnippet({super.key});
#override
State<TestSnippet> createState() => _TestSnippetState();
}
class _TestSnippetState extends State<TestSnippet> {
final PageController controller = PageController();
int itemCount = 44;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(
child: PageView.builder(
itemCount: itemCount ~/ 3,
controller: controller,
itemBuilder: (context, index) => Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(
index.toString(),
),
Text("${index + 1}"),
Text("${index + 2}"),
],
),
),
),
SmoothPageIndicator(
count: itemCount ~/ 3,
controller: controller,
)
],
),
);
}
}

Screen Shift to Left using Flutter Reaction Button Pub

I'm gonna use flutter reaction button plugin on new my app.
But when I put the reaction button, and push the icon, Preview icon box is shown to right side and main view is moved to left site.1
Here is the Item Codes to show pics and reaction button.
class CommunicationItem extends StatefulWidget {
const CommunicationItem({#required this.communication});
final Communication communication;
#override
_CommunicationItemState createState() => _CommunicationItemState();
}
class _CommunicationItemState extends State<CommunicationItem> {
#override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
elevation: 2,
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(5)),
child: Column(
children: [
Image.asset('assets/images/showImage.png'),
spaceH8,
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Row(
children: [
FlutterReactionButtonCheck(
onReactionChanged: (reaction, index, isChecked) {
print('reaction selected index test: $index');
},
reactions: data.reactions,
initialReaction: data.defaultInitialReaction,
selectedReaction: data.reactions[0],
),
I know the other solution like this link.
https://githubmemory.com/repo/GeekAbdelouahed/flutter-reaction-button/issues/22
But I don't know the solution by codes.
Please help it.
V/r,
This happened because the transition of the CupertinoPageRoute, try to set the page transition to none.

Animate two containers width using Animated container

I have two widgets,
Widget FirstWidget()
{
return Container(
height: 40,
child: new Text('Food Products',style: TextStyle(color: Colors.gray))
decoration: new BoxDecoration(
borderRadius: new BorderRadius.all(new Radius.circular(4.0)),
border: Border.all(width: 2, color:Colors.gray)),
padding: new EdgeInsets.all(12.0),
)
}
Widget SecondWidget()
{
return Container(
height: 40,
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Food Products',
TextStyle(color: Colors.blue),
SizedBox(width:8),
Icon(Icons.check,color:Colors.blue ,size: 24,)
],),
decoration: new BoxDecoration(
borderRadius: new BorderRadius.all(new Radius.circular(4.0)),
border: Border.all(width: 2, color:Colors.red)),
padding: new EdgeInsets.all(12.0),
)
}
Now, i need to animate the two widgets based on selection,
class BasicProduct extends StatefulWidget {
final String name;
final bool isSelected;
BasicProduct({Key key, #required this.name, this.isSelected = false});
#override
State<StatefulWidget> createState() => _BasicProduct(name, isSelected);
}
class _BasicProduct extends State<BasicProduct> {
final String name;
bool _isSelected;
_BasicProduct(this.name, this._isSelected);
#override
Widget build(BuildContext context) {
Widget _myAnimatedWidget = _isSelected ? SecondWidget() : FirstWidget();
return new GestureDetector(
onTap: () {
setState(() {
_isSelected = !_isSelected;
_myAnimatedWidget =
_isSelected
? SecondWidget()
: FirstWidget();
});
},
child: AnimatedContainer(
key : _animatedContainerKey
duration: const Duration(seconds: 1),
width: boxWidth
child: _myAnimatedWidget
);
}
}
How to get the width for the two widgets to animate?
Tried Calling WidgetsBinding.instance.addPostFrameCallback((_)=> getSize) in the initState to get the Size
getSize()
{
var boxWidth = _animatedContainerKey.currentContext.findRenderObject().size.width;
}
This Size returning for the SecondWidget only, when firstWidget is changed, the Size returning for SecondWidget only.
Can anyone help how to resolve the issue?
I would take a different approach to this. For one, if you can, you should try using the AnimatedCrossFade widget - it automatically handles both the fade and the size change between two different widgets. If you can make it so that instead of having to specify an exact size in your widgets (always a bad idea) they simply fit to the available space, then this should do what you want. However, without more details of what you're actually trying to accomplish with the animation & size of the widgets, it's a bit difficult to help.
If this doesn't work, then you should consider using an AnimationController and a widget inheriting AnimatedWidget. This way, you have complete control over the build process of your two widgets - you can size, position, and fade them however you want with a stack + opacity or whatever else works best for you.
I would normally write out an example but I think it's a bit beyond the scope of this answer, however you can find out an example of an animated widget here: https://api.flutter.dev/flutter/widgets/AnimatedWidget-class.html. If you do need to go down this route and can't figure out, it would help if you could add more details about what it is you're trying to accomplish instead of what you've attempted to do to accomplish it.

Flutter snackbar alternative or easier method than wrapping everything in Scaffold?

I'm working on my first Flutter app (debugging on my Android phone). I have a list with row items. When you long-press the row, it copies the content into the user's clipboard. This is working great!
But I need to let the user know that the content was copied.
I've attempted to follow many tutorials on trying to get the row surrounded by a build method or inside a Scaffold, but I can't get any to work. Is there an alternative method to notifying the user (simply) that something like "Copied!" took place?
Notice the commented out Scaffold.of(... below. It just seems like there must be an easier method to notifying the user other than wrapping everything in a Scaffold. (and when I try, it breaks my layout).
import 'package:flutter/material.dart';
import 'package:my_app/Theme.dart' as MyTheme;
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/services.dart';
class RowRule extends StatelessWidget {
final DocumentSnapshot ruleGroup;
RowRule(this.ruleGroup);
_buildChildren() {
var builder = <Widget>[];
if (!ruleGroup['label'].isEmpty) {
builder.add(new Text(ruleGroup['label'],
style: MyTheme.TextStyles.articleContentLabelTextStyle));
}
if (!ruleGroup['details'].isEmpty) {
builder.add(new Text(ruleGroup['details'],
style: MyTheme.TextStyles.articleContentTextStyle));
}
return builder;
}
#override
Widget build(BuildContext context) {
return new GestureDetector(
onLongPress: () {
Clipboard.setData(new ClipboardData(text: ruleGroup['label'] + " " + ruleGroup['details']));
// Scaffold.of(context).showSnackBar(SnackBar
// (content: Text('text copied')));
},
child: Container(
margin: const EdgeInsets.symmetric(vertical: 3.0),
child: new FlatButton(
color: Colors.white,
padding: EdgeInsets.symmetric(horizontal: 0.0),
child: new Stack(
children: <Widget>[
new Container(
margin: const EdgeInsets.symmetric(
vertical: MyTheme.Dimens.ruleGroupListRowMarginVertical),
child: new Container(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 32.0, vertical: 8.0),
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: _buildChildren(),
),
)),
)
],
),
),
));
}
}
The goal is to have a page like this (see image), which I have, and it works and scrolls...etc, but I cannot get it to work with a Scaffold, and therefore, haven't been able to use the snackbar. Each "Row" (which this file is for) should show a snackbar on longPress.
You can use GlobalKey to make it work the way you want it.
Since I don't have access to your database stuff, this is how I gave you an idea to do it. Copy and paste this code in your class and make changes accordingly. I also believe there is something wrong in your RowRule class, can you just copy the full code I have given you and run?
void main() => runApp(MaterialApp(home: HomePage()));
class HomePage extends StatelessWidget {
final GlobalKey<ScaffoldState> _key = GlobalKey();
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFFFFFFFF).withOpacity(0.9),
key: _key,
body: Column(
children: <Widget>[
Container(
color: Color.fromRGBO(52, 56, 245, 1),
height: 150,
alignment: Alignment.center,
child: Container(width: 56, padding: EdgeInsets.only(top: 12), decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.yellow)),
),
Expanded(
child: ListView.builder(
padding: EdgeInsets.zero,
itemCount: 120,
itemBuilder: (context, index) {
return Container(
color: Colors.white,
margin: const EdgeInsets.all(4),
child: ListTile(
title: Text("Row #$index"),
onLongPress: () => _key.currentState
..removeCurrentSnackBar()
..showSnackBar(SnackBar(content: Text("Copied \"Row #$index\""))),
),
);
},
),
),
],
),
);
}
}
These is a simple plugin replacement for the Snackbar named "Flushbar".
You can get the plugin here - https://pub.dartlang.org/packages/flushbar
You don't have to take care of any wrapping of widgets into scaffold also you get a lot of modifications for you like background gradient, adding forms and so on into Snackbar's and all.
Inside your onLongPressed in GestureDetectore you can do this.
onLongPressed:(){
Clipboard.setData(new ClipboardData(text: ruleGroup['label'] + " " + ruleGroup['details']));
Flushbar(
message: "Copied !!",
duration: Duration(seconds: 3),
)..show(context);
}
This will display the snackbar in you app where you would want to see it also you can get a lot of modification available to you so the you can make it look as per your app.
There are couple of things you need to do, like use onPressed property of the FlatButton it is mandatory to allow clicks, wrap your GestureDetector in a Scaffold. I have further modified the code so that it uses GlobalKey to make things easy for you.
Here is the final code (Your way)
class RowRule extends StatelessWidget {
final GlobalKey<ScaffoldState> globalKey = GlobalKey();
final DocumentSnapshot ruleGroup;
RowRule(this.ruleGroup);
_buildChildren() {
var builder = <Widget>[];
if (!ruleGroup['label'].isEmpty) {
builder.add(new Text(ruleGroup['label'], style: MyTheme.TextStyles.articleContentLabelTextStyle));
}
if (!ruleGroup['details'].isEmpty) {
builder.add(new Text(ruleGroup['details'], style: MyTheme.TextStyles.articleContentTextStyle));
}
return builder;
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: globalKey,
body: GestureDetector(
onLongPress: () {
Clipboard.setData(new ClipboardData(text: ruleGroup['label'] + " " + ruleGroup['details']));
globalKey.currentState
..removeCurrentSnackBar()
..showSnackBar(SnackBar(content: Text('text copied')));
},
child: Container(
margin: const EdgeInsets.symmetric(vertical: 3.0),
child: new FlatButton(
onPressed: () => print("Handle button press here"),
color: Colors.white,
padding: EdgeInsets.symmetric(horizontal: 0.0),
child: new Stack(
children: <Widget>[
new Container(
margin: const EdgeInsets.symmetric(vertical: MyTheme.Dimens.ruleGroupListRowMarginVertical),
child: new Container(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 32.0, vertical: 8.0),
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: _buildChildren(),
),
),
),
)
],
),
),
),
),
);
}
}
I made a dropdown banner package on pub that allows you to easily notify users of errors or confirmation of success. It's a work in progress as I continue to add visually rich features.
I am not sure if your build() method is completed or you are yet to change it, because it consist of many widgets which are just redundant. Like there is no need to have Container in Container and further Padding along with a FlatButton which would make complete screen clickable. Also having Column won't be a good idea because your screen may overflow if you have more data. Use ListView instead.
So, if you were to take my advice, use this simple code that should provide you what you are really looking for. (See the build() method is of just 5 lines.
class RowRule extends StatelessWidget {
final GlobalKey<ScaffoldState> globalKey = GlobalKey();
final DocumentSnapshot ruleGroup;
RowRule(this.ruleGroup);
_buildChildren() {
var builder = <Widget>[];
if (!ruleGroup['label'].isEmpty) {
builder.add(
ListTile(
title: Text(ruleGroup['label'], style: MyTheme.TextStyles.articleContentLabelTextStyle),
onLongPress: () {
globalKey.currentState
..removeCurrentSnackBar()
..showSnackBar(SnackBar(content: Text("Clicked")));
},
),
);
}
if (!ruleGroup['details'].isEmpty) {
builder.add(
ListTile(
title: Text(ruleGroup['details'], style: MyTheme.TextStyles.articleContentTextStyle),
onLongPress: () {
globalKey.currentState
..removeCurrentSnackBar()
..showSnackBar(SnackBar(content: Text("Clicked")));
},
),
);
}
return builder;
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: globalKey,
body: ListView(children: _buildChildren()),
);
}
}
I read your comments on all answers and here is my conslusion:
You need ScaffoldState object that is just above the widget in tree to show Snackbar. You can either get it through GlobalKey as many have suggested. Fairly simple if the Scaffold is created inside build of the widget, but if it is outside the widget (in your case) then it becomes complicated. You need to pass that key, wherever you need it through Constructor arguments of child widgets.
Scaffold.of(context) is a very neat way to just do that. Just like an InheritedWidget, Scaffold.of(BuildContext context) gives you access of the closest ScaffoldState object above the tree. Else it could be a nightmare to get that instance (by passing it through as constructor arguments) if your tree was very deep.
Sorry, to disappoint but I don't think there is any better or cleaner method than this, if you want to get the ScaffoldState that is not built inside build of that widget. You can call it in any widget that has Scaffold as a parent.