setState() or markNeedsBuild() called during build when playing rive animation - flutter

I got error setState() or markNeedsBuild() called during build while I try to invoke this function:
void togglePlay() {
if (_controller == null) {
return;
}
setState(() => _controller.isActive = !_controller.isActive);
}
Error doesn't not occur when I use it with button as a onTap function. But I need to invoke only with other functions.
Here is complete code:
void togglePlay() {
if (_controller == null) {
return;
}
setState(() => _controller.isActive = !_controller.isActive);
}
bool get isPlaying => _controller?.isActive ?? false;
Artboard _riveArtboard;
RiveAnimationController _controller;
// rive
#override
void initState() {
print('guess position');
// rive
try {
rootBundle.load('assets/match.riv').then(
(data) async {
final file = RiveFile.import(data);
final artboard = file.mainArtboard;
artboard.addController(_controller = SimpleAnimation('Animation 1'));
_controller.isActive = false;
setState(() => _riveArtboard = artboard);
},
);
} catch (e) {
print(e.toString());
}
// rive
super.initState();
}
List top = [];
List left = [];
List rotation = [];
next() {
List correctT = [];
List correct = [];
for (int n = 0; n < widget.rotation.length; n++) {
for (int m = 0; m < widget.rotation.length; m++) {
if (rotation[m] == widget.rotation[n] &&
top[m] < widget.top[n] + 45 &&
top[m] > widget.top[n] - 45 &&
left[m] < widget.left[n] + 45 &&
left[m] > widget.left[n] - 45) {
log('widget match $m qgueals match $n');
if (!correctT.contains(n) && !correct.contains(m)) {
correctT.add(n);
correct.add(m);
}
}
}
}
if (correctT.length == widget.rotation.length) {
log('nice');
togglePlay(); // FUNCTION CAUSING ERROR
}
}
nav() {
SchedulerBinding.instance.addPostFrameCallback((_) {
Navigator.push(
context, new MaterialPageRoute(builder: (context) => RandomPos(2)));
});
}
bool created = false;
createList() {
if (!created) {
for (int i = 0; i < widget.top.length; i++) {
top.add(h - 250);
left.add(50);
rotation.add(0);
}
setState(() {
created = true;
});
}
}
match(int i) {
return Positioned(
top: top[i].toDouble(),
left: left[i].toDouble(),
child: RotationTransition(
turns: new AlwaysStoppedAnimation(rotation[i] / 360),
child: GestureDetector(
onTap: () {
rotation[i] = rotation[i] + 45;
setState(() {});
next();
},
onDoubleTap: () {
rotation[i] = rotation[i] - 45;
setState(() {});
next();
},
child: Draggable(
data: 10,
onDragUpdate: (details) {
top[i] = top[i] + details.delta.dy;
left[i] = left[i] + details.delta.dx;
setState(() {});
},
onDragEnd: next(),
child: Container(
height: 250,
width: 250,
child: _riveArtboard == null
? const SizedBox()
: Rive(artboard: _riveArtboard),
),
feedback: Container()),
),
));
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: createList(),
builder: (context, snapshot) {
return Container(
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.fill,
image: AssetImage('wood${widget.wood}.jpg'))),
child: Stack(
children: [for (int i = 0; i < rotation.length; i++) match(i)],
),
);
}),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.white,
child: Text(
'next',
style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold),
),
onPressed: () => nav()),
);
}
}
Thank you if you could have a look and help me in solving
A few soluttion for typical "set state called during rebuild" didnt work or caused other errors

This error comes when we are calling setState during the build phase.
Use addPostFrameCallback which runs after build rendering is done
void togglePlay() {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (_controller == null) {
return;
}
setState(() => _controller.isActive = !_controller.isActive);
});
}
OR
Timer.run(() {
if (_controller == null) {
return;
}
setState(() => _controller.isActive = !_controller.isActive);
});

Related

How to pass parameter functions to widget stored within a map<int, widget>?

Ive been trying to work this out for awhile now still getting a hang of flutter as its fairly new to me. Im trying to create an animated menu where once i click on the hamburger icon, it opens the menu and when choosing options within the menu, the screen changes. Im having an issue with passing the callback function for each screen to change menuOpen to true using setState. Since I cannot add the callback function to the screen when adding it to the map, what would i need to do in order to send a function as a parameter to change the menuOpen value to open or close the menu. When i add the call back function in the initialisation it says The instance member 'setState' can't be accessed in an initializer. Try replacing the reference to the instance member with a different expression
BuildPageStack builds the screen based on the current index of the map.
Widget buildPageStack(int place, List<Widget> pageList) {
final deviceWidth = MediaQuery.of(context).size.width;
return AnimatedPositioned(
duration: duration,
top: menuOpen ? deviceWidth * 0.10 : 0.0,
bottom: 0,
left: menuOpen ? deviceWidth * 0.40 - (place * 30) : 0.0,
right: menuOpen ? deviceWidth * -0.130 + (place * 30) : 0.0,
child: ScaleTransition(
scale: scaleAnimations[place],
child: GestureDetector(
onTap: () {
if (!mounted)
return;
else if (menuOpen) {
setState(() {
menuOpen = false;
animationController.reverse();
});
}
},
child: AbsorbPointer(
absorbing: menuOpen,
child: Stack(
children: [
Material(
animationDuration: duration,
borderRadius: BorderRadius.circular(menuOpen ? 20.0 : 0.0),
child: pageList[place]),
// Container(
// color: Theme.of(context).primaryColor.withOpacity(0.5),
// ),
],
),
),
),
),
);
}
initState method
void initState() {
super.initState();
animationController = AnimationController(vsync: this, duration: duration);
scaleAnimations = [
Tween<double>(begin: 1.0, end: 0.8).animate(animationController),
Tween<double>(begin: 1.0, end: 0.7).animate(animationController),
Tween<double>(begin: 1.0, end: 0.6).animate(animationController),
];
animationController.forward();
adminScreenSnapshot = adminpages.values.toList();
userScreenSnapshot = userpages.values.toList();
}
Map initialization and call, if the user is an admin it shows 3 screens, if user it shows 2
Map<int, Widget> adminpages = {
0: Main_Foster(menuCallback: (){
setState(() {
menuOpen = true;
animationController.forward();
});}),
1: ProfileScreen(menuCallback: (){
setState(() {
menuOpen = true;
animationController.forward();
});}),
2: CMS(menuCallback: (){
setState(() {
menuOpen = true;
animationController.forward();
});}),
};
Map<int, Widget> userpages = {
0: Main_Foster(menuCallback: (){
setState(() {
menuOpen = true;
animationController.forward();
});}),
1: ProfileScreen(menuCallback: () {
setState(() {
menuOpen = true;
animationController.forward();
});}),
};
List<Widget> adminScreenSnapshot;
List<Widget> userScreenSnapshot;
List<Widget> finalPageStack() {
List<Widget> returnStack = [];
returnStack.add(MenuScreen(
menuCallback: (selectedIndex) {
if (widget.isAdmin) {
setState(() {
adminScreenSnapshot = adminpages.values.toList();
final selectedWidget = adminScreenSnapshot.removeAt(selectedIndex);
adminScreenSnapshot.insert(0, selectedWidget);
});
} else {
setState(() {
userScreenSnapshot = userpages.values.toList();
final selectedWidget = userScreenSnapshot.removeAt(selectedIndex);
userScreenSnapshot.insert(0, selectedWidget);
});
}
},
));
if (widget.isAdmin) {
adminScreenSnapshot
.asMap()
.entries
.map((screen) => buildPageStack(screen.key, adminScreenSnapshot))
.toList()
.reversed
..forEach((screen) {
returnStack.add(screen);
});
} else {
userScreenSnapshot
.asMap()
.entries
.map((screen) => buildPageStack(screen.key, userScreenSnapshot))
.toList()
.reversed
..forEach((screen) {
returnStack.add(screen);
});
}
return returnStack;
}
FinalPageStack returns the required stack
List<Widget> finalPageStack() {
List<Widget> returnStack = [];
returnStack.add(MenuScreen(
menuCallback: (selectedIndex) {
if (widget.isAdmin) {
setState(() {
adminScreenSnapshot = adminpages.values.toList();
final selectedWidget = adminScreenSnapshot.removeAt(selectedIndex);
adminScreenSnapshot.insert(0, selectedWidget);
});
} else {
setState(() {
userScreenSnapshot = userpages.values.toList();
final selectedWidget = userScreenSnapshot.removeAt(selectedIndex);
userScreenSnapshot.insert(0, selectedWidget);
});
}
},
));
if (widget.isAdmin) {
adminScreenSnapshot
.asMap()
.entries
.map((screen) => buildPageStack(screen.key, adminScreenSnapshot))
.toList()
.reversed
..forEach((screen) {
returnStack.add(screen);
});
} else {
userScreenSnapshot
.asMap()
.entries
.map((screen) => buildPageStack(screen.key, userScreenSnapshot))
.toList()
.reversed
..forEach((screen) {
returnStack.add(screen);
});
}
return returnStack;
}

Flutter weird bug with widget function, sometimes in runs 2 times instead of one

This function have futureBuilder inside and returns another function that returns ListView.
Also I do not want the user to download information from the Internet every time they visit the page, so I put the information downloaded from the Internet into a static variable in class, so i make if else. If this static variable length != 0 , it return me listView instead of make server request with futureBuilder
function that works 2 times instead of one is ListView body
And also this bug created after i added purchases to my application, maybe something wrong there. I don't have any ideas why this happend
this is my complete code
class AddCheckList extends StatefulWidget {
const AddCheckList({Key? key}) : super(key: key);
#override
_AddCheckListState createState() => _AddCheckListState();
}
class _AddCheckListState extends State<AddCheckList> {
String xQueryReqestForCheckListNames = "element CheckListList {for \$a in PACK/OBJECT where (\$a/#inUse = 'True') order by \$a/#name return element CheckList {attribute name {\$a/#name}, attribute sPaid {\$a/#isPaid},attribute oid {\$a/#oid} }}";
String serverLink = "http...";
int addCheckListMethod = 0;
// if addCheckListMethod == 0, then data will download from server and checkLists will be added from server xml data
// if addCheckListMethod == 1, then data will download from assets, and checkLists will be added from assets xml data with getXmlData function
final InAppPurchase _inAppPurchase = InAppPurchase.instance;
final String _productID = '1d7ea644f690ffa';
bool _available = true;
List<ProductDetails> _products = [];
List<PurchaseDetails> _purchases = [];
StreamSubscription<List<PurchaseDetails>>? _subscription;
#override
void initState() {
final Stream<List<PurchaseDetails>> purchaseUpdated = _inAppPurchase.purchaseStream;
_subscription = purchaseUpdated.listen((purchaseDetailsList) {
setState(() {
_purchases.addAll(purchaseDetailsList);
_listenToPurchaseUpdated(purchaseDetailsList);
});
}, onDone: () {
_subscription!.cancel();
}, onError: (error) {
_subscription!.cancel();
});
_initialize();
super.initState();
}
#override
void dispose() {
_subscription!.cancel();
super.dispose();
}
void _initialize() async {
_available = await _inAppPurchase.isAvailable();
List<ProductDetails> products = await _getProducts(
productIds: Set<String>.from(
[_productID],
),
);
setState(() {
_products = products;
});
}
void _listenToPurchaseUpdated(List<PurchaseDetails> purchaseDetailsList) {
purchaseDetailsList.forEach((PurchaseDetails purchaseDetails) async {
switch (purchaseDetails.status) {
case PurchaseStatus.pending:
// _showPendingUI();
break;
case PurchaseStatus.purchased:
case PurchaseStatus.restored:
// bool valid = await _verifyPurchase(purchaseDetails);
// if (!valid) {
// _handleInvalidPurchase(purchaseDetails);
// }
break;
case PurchaseStatus.error:
print(purchaseDetails.error!);
// _handleError(purchaseDetails.error!);
break;
default:
break;
}
if (purchaseDetails.pendingCompletePurchase) {
await _inAppPurchase.completePurchase(purchaseDetails);
}
});
}
Future<List<ProductDetails>> _getProducts({required Set<String> productIds}) async {
ProductDetailsResponse response = await _inAppPurchase.queryProductDetails(productIds);
return response.productDetails;
}
void _subscribe({required ProductDetails product}) {
final PurchaseParam purchaseParam = PurchaseParam(productDetails: product);
_inAppPurchase.buyNonConsumable(
purchaseParam: purchaseParam,
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(padding: EdgeInsets.all(14.0), child: listViweBody(addCheckListMethod, serverLink, xQueryReqestForCheckListNames, _products,_subscribe)),
);
}
}
Widget listView(addCheckListMethod, serverLink, product,_subscribe) {
return ListView.separated(
itemCount: CheckListModel.checkListModelNamesFromServer.length,
itemBuilder: (BuildContext context, int index) {
if (CheckListModel.checkListModelNamesFromServer[index].ispaid == false) {
return InkWell(
onTap: () async {
CheckListModel checkList = CheckListModel('', 0, '', 0, 0, 0, '', [], '');
if (addCheckListMethod == 0) {
String xQueryReqestForCheckList = 'for \$a in PACK/OBJECT where \$a/#name="${CheckListModel.checkListModelNamesFromServer[index].name}" return \$a';
var data = await CheckListModel.getDataFromServer(xQueryReqestForCheckList, serverLink);
CheckListModel.addCheckListFromServer(checkList, data);
} else {
CheckListModel.addCheckList(index, checkList);
}
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Добавить описание"),
content: Container(
decoration: BoxDecoration(border: Border.all(color: Color.fromARGB(Desing.colorFromARGBBtn[0], Desing.colorFromARGBBtn[1], Desing.colorFromARGBBtn[2], Desing.colorFromARGBBtn[3]), width: 1), borderRadius: BorderRadius.circular(10)),
child: TextField(
decoration: new InputDecoration.collapsed(hintText: "Описание", border: InputBorder.none),
maxLines: null,
onChanged: (String value) async {
checkList.description = value;
},
),
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("Отменить"),
),
TextButton(
onPressed: () async {
await checkList.writeToFile(checkList.toJson());
CheckListModel.checkLists.add(checkList);
Navigator.pushNamed(context, '/beforeMainCheckList', arguments: {'index': CheckListModel.checkLists.length - 1});
},
child: Text('Добавить'))
],
);
});
},
child: Container(
decoration: BoxDecoration(border: Border.all(color: Color.fromARGB(Desing.colorFromARGBBtn[0], Desing.colorFromARGBBtn[1], Desing.colorFromARGBBtn[2], Desing.colorFromARGBBtn[3]), width: 1), borderRadius: BorderRadius.circular(10)),
width: 50,
height: 80,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"${CheckListModel.checkListModelNamesFromServer[index].name}",
style: TextStyle(fontSize: 30),
)
],
),
),
);
} else {
if (product.length != 0) {
return showPurchaseCheckLists(product, index,_subscribe);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}
},
separatorBuilder: (BuildContext context, int index) {
return Container(
height: 14,
);
});
}
Widget showPurchaseCheckLists(product, index,_subscribe) {
int getCurrentProduct() {
String? checkListModelid = CheckListModel.checkListModelNamesFromServer[index].checkListId?.toLowerCase();
int indexx = 0;
for (int i = 0; i < product.length; i++) {
if (checkListModelid == product[i].id) {
indexx = i;
}
}
return indexx;
}
return InkWell(
child: Container(
decoration: BoxDecoration(border: Border.all(color: Color.fromARGB(Desing.colorFromARGBBtn[0], Desing.colorFromARGBBtn[1], Desing.colorFromARGBBtn[2], Desing.colorFromARGBBtn[3]), width: 1), borderRadius: BorderRadius.circular(10), color: Color.fromARGB(255, 240, 240, 240)),
width: 50,
height: 80,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"${CheckListModel.checkListModelNamesFromServer[index].name}",
style: TextStyle(fontSize: 25),
),
Text("Купить за ${product[getCurrentProduct()].price}")
],
),
),
onTap: () {
_subscribe(product: product[getCurrentProduct()]);
},
);
}
Widget listViweBody(addCheckListMethod, serverLink, xQueryReqestForCheckListNames, product,_subscribe) {
if (CheckListModel.checkListModelNamesFromServer.length == 0) {
return FutureBuilder(
future: CheckListModel.getDataFromServer(xQueryReqestForCheckListNames, serverLink),
builder: (context, data) {
if (data.connectionState == ConnectionState.done) {
return listView(addCheckListMethod, serverLink, product,_subscribe);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
});
} else {
return listView(addCheckListMethod, serverLink, product,_subscribe);
}
}

Slider widget does not work on iOS and Web

I have an audio player in my app. I used Slider.adaptive to make the seek bar. Although It works well on Android, it gives me this error on iOS and the Web:
════════ Exception caught by widgets library
═══════════════════════════════════ Assertion failed:
../…/cupertino/slider.dart:332 value != null && value >= 0.0 && value
<= 1.0 is not true
The relevant error-causing widget was Slider
Here is how my code looks like:
class PlayBackButtons extends StatefulWidget {
#override
_PlayBackButtonsState createState() => _PlayBackButtonsState();
}
class _PlayBackButtonsState extends State<PlayBackButtons> {
bool _isPlaying = false;
AudioPlayer _audioPlayer;
Duration _duration = new Duration();
Duration _position = new Duration();
int result = 0;
int tutsLenght;
int index;
#override
void initState() {
super.initState();
_audioPlayer = AudioPlayer();
Future.delayed(Duration.zero, () {
Provider.of<Tutorials>(context, listen: false).resetIndex();
});
}
#override
void dispose() {
_cleanup();
super.dispose();
}
double progress() => max() > (_position?.inMilliseconds ?? 0).toDouble()
? (_position?.inMilliseconds ?? 0).toDouble()
: 0.0;
double max() => (_duration.inMilliseconds ?? 0.0).toDouble();
void _cleanup() {
if (result == 1) {
_audioPlayer.stop();
_audioPlayer.dispose();
Provider.of<Tutorials>(context).resetIndex();
}
}
void _play(String url) async {
result = await _audioPlayer.play(url);
setState(() => _isPlaying = true);
if (result == 1) {
_audioPlayer.onDurationChanged.listen((Duration dd) {
setState(() {
_duration = dd;
});
});
_audioPlayer.onAudioPositionChanged.listen((Duration dd) {
setState(() {
_position = dd;
});
});
} else {
print('audio player crashed');
}
}
void _pause() async {
await _audioPlayer.pause();
setState(() => _isPlaying = false);
}
void _next() async {
await _audioPlayer.stop();
setState(() => _isPlaying = false);
if (Provider.of<Tutorials>(context).index < tutsLenght) {
} else {
Navigator.pop(context);
}
}
void _previous() {
if (Provider.of<Tutorials>(context).index != 0) {
_audioPlayer.stop();
setState(() => _isPlaying = false);
Provider.of<Tutorials>(context).decrementIndex();
}
}
#override
Widget build(BuildContext context) {
index = Provider.of<Tutorials>(context).index;
final tuts = Provider.of<Tutorials>(context, listen: false).tutsOfASection;
tutsLenght = tuts.length;
final url = tuts[index].audio;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Slider.adaptive(
min: 0.0,
value: progress(),//_position.inSeconds.toDouble(),
max: max(),//_duration.inSeconds.toDouble(),
onChanged: (double value) {
setState(() => _audioPlayer.seek(Duration(seconds: value.toInt())));
},
),
Row(
children: [
IconButton(
icon: Icon(Icons.skip_previous),
onPressed: () => _previous(),
),
IconButton(
icon: _isPlaying ? Icon(Icons.pause) : Icon(Icons.play_arrow),
onPressed: () {
if (_isPlaying) {
_pause();
} else {
_play(url);
}
},
),
IconButton(
icon: Icon(Icons.skip_next),
onPressed: () => _next(),
),
],
mainAxisAlignment: MainAxisAlignment.center,
),
],
);
}
}

flutter animated image is so laggy

import 'dart:math';
import 'package:audioplayers/audio_cache.dart';
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:image_sequence_animator/image_sequence_animator.dart';
import 'package:kane/model/KaneType.dart';
class Kane extends StatefulWidget {
Kane({Key key}) : super(key: key);
#override
KaneState createState() => KaneState();
}
class KaneState extends State<Kane> {
int _noseCount = 0;
bool _isHover = false;
bool _isPlaying = false;
KaneType _kaneType = KaneType.Kane;
List<GlobalKey<ImageSequenceAnimatorState>> animatorKeyList = [
GlobalKey(),
GlobalKey(),
GlobalKey(),
GlobalKey(),
GlobalKey()
];
AudioCache _cache = AudioCache();
AudioPlayer _player;
#override
Widget build(BuildContext context) {
ScreenUtil.init(context);
Widget kane;
switch (_kaneType) {
case KaneType.Kane:
kane = _kaneAnimation("kane", 8, 15, animatorKeyList[0], true);
break;
case KaneType.Ricardo:
kane = _kaneAnimation("ricardo", 212, 15, animatorKeyList[1], false);
break;
case KaneType.SexyKane:
kane = _kaneAnimation("sexyKane", 9, 15, animatorKeyList[2], true);
break;
case KaneType.MoemoeKane:
kane = _kaneAnimation("moemoe", 137, 24, animatorKeyList[3], false);
break;
default:
kane = _kaneAnimation("hanwha", 203, 24, animatorKeyList[4], false);
}
return Stack(
children: <Widget>[
Align(
alignment: Alignment.bottomCenter,
child: Container(
height: ScreenUtil().setHeight(800),
child: kane,
),
),
_kaneType == KaneType.Kane && !_isPlaying
? Positioned(
top: ScreenUtil().setHeight(918),
left: ScreenUtil().setWidth(506),
right: ScreenUtil().setWidth(506),
child: InkWell(
child: _isHover
? ColorFiltered(
colorFilter: ColorFilter.mode(
Colors.grey[400], BlendMode.modulate),
child: Image.asset(
"assets/kane/kane/nose.webp",
width: ScreenUtil().setHeight(40),
height: ScreenUtil().setHeight(40),
fit: BoxFit.contain,
))
: Image.asset(
"assets/kane/kane/nose.webp",
width: ScreenUtil().setHeight(40),
height: ScreenUtil().setHeight(40),
fit: BoxFit.contain,
),
onTap: () {
setState(() => _isHover = false);
if (++_noseCount >= Random().nextInt(5) + 3) {
_noseCount = 0;
_cache.play('music/igonan.m4a');
} else {
_cache.play('music/bbolong.mp3');
}
},
onTapDown: (_) => setState(() => _isHover = true),
onTapCancel: () => setState(() => _isHover = false),
),
)
: Container()
],
);
}
Widget _kaneAnimation(String name, double frameCount, double fps,
GlobalKey<ImageSequenceAnimatorState> key, bool rewind) {
bool first = true;
return InkWell(
child: ImageSequenceAnimator(
"assets/kane/$name",
"$name",
0,
frameCount.toString().length - 2,
"webp",
frameCount,
key: key,
fps: fps,
isAutoPlay: false,
color: null,
onFinishPlaying: (animator) {
if (rewind && first) {
key.currentState.rewind();
first = false;
} else {
setState(() {
_isPlaying = false;
first = true;
});
key.currentState.reset();
}
},
),
onTap: () async {
if (!_isPlaying) {
setState(() {
_isPlaying = true;
});
_player = await _cache.play('music/$name.mp3');
key.currentState.play();
} else {
setState(() {
_isPlaying = false;
first = true;
});
_player.stop();
key.currentState.reset();
}
},
);
}
changeKane(KaneType kaneType) {
setState(() => _kaneType = kaneType);
}
}
I made an animation, but some devices are lagging.
https://www.youtube.com/watch?v=oScBgRUp1B8&feature=youtu.be
The animation is lagging too much.
InkWell(
child: Image.asset(
"assets/kane/moemoe/moemoe$_imageCount.webp",
),
onTap: () async {
for (int j = 1; j <= 136; j++) {
await Future.delayed(const Duration(milliseconds: 42), () {
setState(() => _imageCount++);
});
}
},
)
It's lagging even in this way. Also lag seems to only appear on some smartphones such as Galaxy a30, s7, and s9. p.s I am not good at English.
enter image description here The total size of the image is 3mb.
You are calling setState(() => _imageCount++); inside a for loop. Be careful when using this method! Calling setState, according to the documentation:
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 means, this for loop:
for (int j = 1; j <= 136; j++) {
await Future.delayed(const Duration(milliseconds: 42), () {
setState(() => _imageCount++);
});
}
Might be the cause of the lag. Every loop, you are telling your application that something has changed, and it needs to rebuild all the elements inside the build() function. And that might not be the only cause of lag, as I see you use setState((){}) a lot.

AnimatedList is not containing initial data which is loaded using SharedPreferences

I have an AnimatedList whose initial items are getting added before loading data from SharedPreferences. Is there a way, I can load data from device first and then build the screen.
Let's say I'm loading data into myList by for loop using SharedPreferences. And then using the data from myList to create AnimatedList. The problem I'm facing is, the data that was loaded is not Showing in AnimatedList. AnimatedList is getting build before loading the data.
import 'package:material_todo/index.dart';
class TaskScreen extends StatefulWidget {
TaskScreen({this.taskProvider});
final TaskProvider taskProvider;
#override
_TaskScreenState createState() => _TaskScreenState();
}
class _TaskScreenState extends State<TaskScreen>
with SingleTickerProviderStateMixin {
#override
void initState() {
super.initState();
widget.taskProvider.initialiseController(this);
}
String taskRemaining(List tasks) {
var remain = 0;
for (var i in tasks) {
if (i[2] == false) {
remain += 1;
}
}
return '$remain';
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: kWhiteColor,
appBar: widget.taskProvider.editTask
? AppBar(
backgroundColor: Colors.black54,
title: Text('Edit mode'),
centerTitle: false,
leading: IconButton(
onPressed: () {
widget.taskProvider.selectTask(false);
widget.taskProvider.toggleAddTask(false);
widget.taskProvider.toggleEditTask(false);
},
icon: Icon(
Icons.close,
color: Colors.white,
),
),
actions: <Widget>[
IconButton(
onPressed: () {
widget.taskProvider.editingTask();
},
icon: Icon(
Icons.edit,
color: Colors.white,
),
),
IconButton(
onPressed: () {
widget.taskProvider.deleteTask();
widget.taskProvider.toggleEditTask(false);
widget.taskProvider.toggleAddTask(false);
},
icon: Icon(
Icons.delete,
color: Colors.white,
),
),
],
)
: AppBar(
title: Text(
'Tasks',
),
actions: <Widget>[
IconButton(
onPressed: () {
// Navigator.of(context).push(
// MaterialPageRoute(
// builder: (context) =>
// SettingsScreen(provider: taskProvider),
// ),
// );
widget.taskProvider.clearData();
},
icon: Icon(
Icons.settings,
color: Colors.white,
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
if (widget.taskProvider.showAddTask) {
widget.taskProvider.toggleAddTask(false);
} else {
widget.taskProvider.toggleAddTask(true);
}
try {
widget.taskProvider.selectTask(false);
widget.taskProvider.toggleEditTask(false);
} catch (e) {
print(e);
}
},
child: RotationTransition(
turns: Tween(begin: 0.0, end: 0.125)
.animate(widget.taskProvider.rotationalController),
child: Icon(
Icons.add,
),
),
),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
widget.taskProvider.showAddTask
? BuildTaskInput(widget.taskProvider)
: Container(
height: 0,
width: 0,
),
Padding(
padding:
const EdgeInsets.only(left: 30.0, top: 30.0, bottom: 20.0),
child: Text(
taskRemaining(widget.taskProvider.tasks) + ' tasks remaining',
style: TextStyle(
fontSize: 20.0,
color: Colors.grey[800],
fontWeight: FontWeight.w500,
),
),
),
Expanded(
child: AnimatedList(
key: widget.taskProvider.listKey,
initialItemCount: widget.taskProvider.tasks.length,
itemBuilder: (context, index, animation) {
return SizeTransition(
sizeFactor: animation,
child: TaskTile(
title: widget.taskProvider.tasks[index][0],
due: widget.taskProvider.tasks[index][1],
isChecked: widget.taskProvider.tasks[index][2],
isSelected: widget.taskProvider.tasks[index][3],
changeValue: (newValue) {
widget.taskProvider.checkTask(newValue, index);
},
editTask: () {
widget.taskProvider.setTileIndex(index);
widget.taskProvider.selectTask(true);
widget.taskProvider.toggleEditTask(true);
},
),
);
}),
),
],
),
),
);
}
}
import 'package:material_todo/index.dart';
class TaskProvider extends ChangeNotifier {
TaskProvider() {
loadData();
}
//Properties
TextEditingController _textController = TextEditingController();
AnimationController _rotationController;
final GlobalKey<AnimatedListState> _listKey = GlobalKey();
int _tileIndex = 0; //index of selected tile.
bool _showAddTask = false; //whether to show add task container.
bool _editTask = false; // edit task.
bool _deleteCheckTasks = false;
bool _bottomCheckTasks = true;
List<List> _tasks = [
['abc', 'Today', false, false]
]; //stores all the task data.
//Getters
List<List> get tasks => _tasks;
bool get showAddTask => _showAddTask;
bool get editTask => _editTask;
bool get deleteCheckTasks => _deleteCheckTasks;
bool get bottomCheckTasks => _bottomCheckTasks;
int get tileIndex => _tileIndex;
TextEditingController get textController => _textController;
AnimationController get rotationalController => _rotationController;
GlobalKey<AnimatedListState> get listKey => _listKey;
//Setters
void setTileIndex(int index) {
_tileIndex = index;
notifyListeners();
}
void addTask(String title, String due) {
int index = addTaskAtProperIndex(due);
_tasks.insert(index, [
title,
due,
false,
false,
]);
_listKey.currentState.insertItem(index);
notifyListeners();
saveData();
}
void toggleAddTask(bool value) {
_showAddTask = value;
if (!value) {
_rotationController.reverse();
_textController.clear();
} else {
_rotationController.forward();
}
notifyListeners();
print(value);
}
void toggleEditTask(bool value) {
_editTask = value;
notifyListeners();
}
void toggleDeleteTask(bool value) {
_deleteCheckTasks = value;
if (value) {
for (int i = _tasks.length - 1; i >= 0; i--) {
if (_tasks[i][2] == true) {
_tasks.removeAt(i);
}
}
}
notifyListeners();
saveData();
}
void toggleBottomCheckTask(bool value) {
_bottomCheckTasks = value;
if (value) {
for (int i = _tasks.length - 1; i >= 0; i--) {
if (_tasks[i][2] == true) {
_tasks.add(_tasks[i]);
_tasks.removeAt(i);
}
}
}
notifyListeners();
saveData();
}
void checkTask(bool value, int index) {
_tasks[index][2] = value;
if (value && _deleteCheckTasks) {
deleteTask();
}
if (value && _bottomCheckTasks) {
_tasks.add(_tasks[index]);
_tasks.removeAt(index);
}
notifyListeners();
saveData();
}
void selectTask(bool value) {
_tasks[_tileIndex][3] = value;
notifyListeners();
saveData();
}
void deleteTask() {
_tasks.removeAt(_tileIndex);
notifyListeners();
saveData();
}
void replaceTask(String title, String due) {
_tasks.removeAt(_tileIndex);
addTask(title, due);
selectTask(false);
toggleEditTask(false);
notifyListeners();
saveData();
}
void editingTask() {
toggleAddTask(true);
_textController.value = TextEditingValue(text: _tasks[_tileIndex][0]);
}
int addTaskAtProperIndex(String due) {
for (int i = 0; i < _tasks.length; i++) {
if (_tasks[i][1] == due) {
return i;
}
if (due == 'Today') {
return 0;
}
if (due == 'Tomorrow') {
for (int i = _tasks.length - 1; i >= 0; i--) {
if (_tasks[i][1] == 'Today') {
return i + 1;
}
}
return 0;
}
if (due == 'Anytime') {
for (int i = _tasks.length - 1; i >= 0; i--) {
if (_tasks[i][1] == 'Tomorrow') {
return i + 1;
}
}
for (int i = _tasks.length - 1; i >= 0; i--) {
if (_tasks[i][1] == 'Today') {
return i + 1;
}
}
return 0;
}
}
return 0;
}
void initialiseController(TickerProvider ticker) {
_rotationController = AnimationController(
duration: const Duration(milliseconds: 200), vsync: ticker);
}
void saveData() async {
SharedPreferences _prefs = await SharedPreferences.getInstance();
_prefs.setInt('count', _tasks.length);
_prefs.setBool('deleteCheckTasks', _deleteCheckTasks);
_prefs.setBool('bottomCheckTasks', _bottomCheckTasks);
for (int i = 0; i < _tasks.length; i++) {
_prefs.setString('$i 0', _tasks[i][0]);
_prefs.setString('$i 1', _tasks[i][1]);
_prefs.setBool('$i 2', _tasks[i][2]);
}
print('data saved successfully');
}
Future loadData() async {
SharedPreferences _prefs = await SharedPreferences.getInstance();
var _count = _prefs.getInt('count');
if (_count != null) {
for (int i = 0; i < _count; i++) {
var _title = _prefs.getString('$i 0');
var _due = _prefs.getString('$i 1');
var _check = _prefs.getBool('$i 2');
_tasks.add([_title, _due, _check, false]);
}
print('dada loaded successfully');
} else {
print('No data was found');
}
if (_prefs.getBool('deleteCheckTasks') != null) {
_deleteCheckTasks = _prefs.getBool('deleteCheckTasks');
}
if (_prefs.getBool('bottomCheckTasks') != null) {
_bottomCheckTasks = _prefs.getBool('bottomCheckTasks');
}
notifyListeners();
}
Future clearData() async {
SharedPreferences _prefs = await SharedPreferences.getInstance();
_prefs.clear();
}
}