dismiss database data with listview - flutter

now i made the code like this
FutureBuilder(
future: getData2(),
builder: (context, snapshot) {
if (snapshot.hasData) {
List data = snapshot.data;
return ListView.builder(
shrinkWrap: true,
itemCount: data.length,
itemBuilder: (context, index) {
final item = data[index];
return Dismissible(
key: Key(item['loc3']),
onDismissed: (direction) {
setState(() async {
await openData2().then((value) {
value.delete(
'test2',
where: 'loc3 = ?',
whereArgs: ['loc3'],
);
});
});
},
child: ListTile(
title: Text(item['loc3']),
),
);
},
);
but when i dismiss one item
there's some note in console 'Another exception was thrown: setState() callback argument returned a Future.'
in setState i don't return any variable why they say like this?
and when i first delete one item there is more detail error
The following assertion was thrown while notifying listeners for AnimationController:
I/flutter ( 6018): setState() callback argument returned a Future.
I/flutter ( 6018): The setState() method on PositionedTilesState#6d110 was called with a closure or method that
I/flutter ( 6018): returned a Future. Maybe it is marked as "async".
I/flutter ( 6018): Instead of performing asynchronous work inside a call to setState(), first execute the work (without
I/flutter ( 6018): updating the widget state), and then synchronously update the state inside a call to setState().
I/flutter ( 6018): When the exception was thrown, this was the stack:

I think the 1 problem is that you have an async function inside setState. Change it to this:
...
onDismissed: (direction) async {
await openData2().then((value) {
value.delete(
'test2',
where: 'loc3 = ?',
whereArgs: ['loc3'],
);
});
setState(() {});
},
'''
or this (more readable):
...
onDismissed: (direction) {
Future<void> delete() async {
await openData2().then((value) {
value.delete(
'test2',
where: 'loc3 = ?',
whereArgs: ['loc3'],
);
});
}
setState(() {
delete();
});
},
'''
The point is, you can't have async operations performed inside setState.

Related

FutureBuilder update by timer

I don't quite understand how you can update future in FutureBuilder by timer. I tried to create a timer and give it to the future, but it didn't work out and there was an error: type '_Timer' is not a subtype of the 'Future?'
my handler with a request:
Future<ObjectStateInfoModel> read(int id) async {
TransportResponse response = await transport.request(
'get',
RequestConfig(path: path + '($id)'),
TransportConfig(
headers: {},
));
ObjectStateInfoModel objectState = ObjectStateInfoModel.fromJson(response.data);
return objectState;
}
my FutureBuilder:
return FutureBuilder<ObjectStateInfoModel>(
future: logexpertClient.objectsState.read(object.id),
builder: (context, snapshot) {
if (snapshot.hasData) {
final data = snapshot.data!;
on the advice of one of the commentators i converted FutureBuilder to StreamBuilder and created such a stream and then everything works correctly:
stream = Stream.periodic(const Duration(seconds: 5)).asyncMap((_) async {
return logexpertClient.objectsState.read(object.id);
});
Use refreshable_widget, which is built specifically for this.
https://pub.dev/packages/refreshable_widget
Flexible(
child: RefreshableWidget<num>(
initialValue: challenge.userParticipation!.donePercent,
refreshCall: () async {
final challenge =
await cadoo.getChallengeDetail(
id: widget.challengeId,
);
return challenge.userParticipation!.donePercent;
},
builder: (context, value) {
return DonePercentWidget(
percent: value,
);
},
),
),
Pass a refresh call and how often you want to refresh, widget will build whatever on builder method.

Another exception was thrown: Null check operator used on a null value

i need to use a future builder to get the value of a radio button form shared preferences.
but the error occurs when i open the page of the radio button is:
Another exception was thrown: Null check operator used on a null value
the radio button widget is:
FutureBuilder<bool>(
future: getDayNotificationIsOn(),
builder: (context, snapshot) {
return NeumorphicSwitch(
value: snapshot.data!,
style: NeumorphicSwitchStyle(
thumbShape: NeumorphicShape.flat,
activeTrackColor: const Color(0xff257864),
),
onChanged: (value) async {
setState(() {});
//sharedPreferences
await setDayNotificationIsOn(value);
},
);
}),
the function used in future builder to get the value of the radio button:
Future<bool> getDayNotificationIsOn() async {
SharedPreferences _pref = await SharedPreferences.getInstance();
return _pref.getBool('dayNotificationIsOn') ?? false;
}
You should add a check before NeumorphicSwitch to make sure the snapshot has data by adding if (snapshot.hasData).
I guess snapshot.data is null and snapshot.data! is firing this exception

Flutter Bad State No Element

I am trying to delete my data from a database, and proceed to navigate to my homepage if it succeeds.
Below are my code:
StatelessWidget that consist of deleteFromDatabase method which passed an Id(String), an a context:
Consumer<SettingsProvider>(
builder: (context, settingsProvider, child) {
final exerciseSettings = settingsProvider.findById(id);
if (exerciseSettings == null) {
return Center(
child: CircularProgressIndicator(),
);
}
return PreviewExerciseItem(
exerciseSettings: exerciseSettings,
id: id,
navigateToEdit: () =>
_navigateToEditPage(exerciseSettings, context),
deleteFromDatabase: () => _deleteFromDatabase(id, context),
navigateToCountDown: () =>
navigateToCountDownPage(exerciseSettings, context),
);
},
),
_deleteFromDatabase method called from StatelessWidget and shows an AlertDialog to confirm deletion:
void _deleteFromDatabase(String id, context) async {
await showDialog(
context: context,
builder: (context) => new AlertDialog(
title: new Text("Are you sure you want to delete?"),
actions: <Widget>[
new FlatButton(
onPressed: () => Navigator.of(context).pop(false),
child: new Text('No'),
),
new FlatButton(
onPressed: () async {
try {
Navigator.of(context).pop(true);
await Provider.of<SettingsProvider>(context, listen: false)
.deleteFromList(id);
Navigator
.pushNamedAndRemoveUntil(context,HomePage.routeName, (Route route) => route.isFirst);
} catch (e) {
print(e);
}
},
child: new Text('Yes'),
),
],
),
);
}
deleteFromList method From My Provider class:
Future<void> deleteFromList(String id) async{
try{
final _itemIndex = _items.indexWhere((item) => item.id == id);
await _databaseHelper.deleteExercises(id);
_items.removeAt(_itemIndex);
notifyListeners();
}catch(e){
print(e);
}
}
findById from Provider Class:
CustomExercise findById(String id) {
return _items.firstWhere((prod) => prod.id == id);
}
Note: I am able to delete my data successfully from my database, however right before it navigates to my HomePage, an error pops out for a split second as a form of Red Screen: Bad State: No Element
Below are the full error message from my Log:
The following StateError was thrown building Consumer(dirty, dependencies: [_InheritedProviderScope, _InheritedTheme, _LocalizationsScope-[GlobalKey#5ce12]]):
Bad state: No element
The relevant error-causing widget was:
Consumer<SettingsProvider>
When the exception was thrown, this was the stack:
#0 ListMixin.firstWhere (dart:collection/list.dart:150:5)
#1 SettingsProvider.findById (package:workoutapp/providers/settings_provider.dart:12:19)
#2 PreviewExercisePage.build.<anonymous closure> (package:workoutapp/pages/preview_exercise_page.dart:68:55)
#3 Consumer.buildWithChild (package:provider/src/consumer.dart:175:19)
#4 SingleChildStatelessWidget.build (package:nested/nested.dart:260:41)
This is happens when the list is empty or maybe the first element is empty, so you should check the list is not empty.
List list = [];
print(list[0])
is sure you'll receive like this message:
Unhandled exception:
Bad state: No element
#0 List.first (dart:core-patch/growable_array.dart:332:5)
#1 main (file:///C:/Users/onbody/AndroidStudioProjects/nmae/bin/name.dart:9:14)
#2 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:281:32)
#3 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
the solution is:
List list = [];
if (list.isNotEmpty) {
print(list[0]);
} else {
print('the list is empty'!);
}
I hope this is helpful for someone Thanks!
As previously mentioned in the comments, it's likely that checking the values of an empty List causes the error. A workaround for this is to have a checker if the List is empty on both CustomExercise findById(String) and deleteFromList(String).
i.e.
if(_items != null && _items.length > 0)
We were using Drift (formerly Moor) and its watchSingle() method. That will throw this error, if no matching database row is found.
It was very hard to track down since a stream was emitting the error and it had no stack trace attached to it.
The fix was to use watch() with limit(1) instead and skip processing if the result is empty.

How do I prevent Flutter FutureBuilder from firing early?

I'm using the following FutureBuilder to handle fetching 'squad' info from a Firebase database, but the Future is saying it's done before I can process all the data form the database:
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _getUserSquads(),
builder: (ctx, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
} else {
if (_userSquads == null) {...} else {
print(snapshot.connectionState);
return Text(_userSquads.length.toString());
}
}
},
);
... the following two functions are the functions I need to be completed before the FutureBuilder is done:
Future<void> _getUserSquads() async {
print('1');
final squadIdsResult = await _userSquadsRef.once();
print('2');
if (squadIdsResult.value == null) {
print('3');
return;
}
print('4');
_userSquadIds = squadIdsResult.value;
print('5');
final test = await _test();
print('6');
}
Future<void> _test() {
print('7');
_userSquadIds.forEach((key, value) async {
print('itter');
final result = await _squadsRef.child(key).once();
_userSquads.add(result.value);
print(result.value);
print(_userSquads);
});
print('8');
print('9');
}
The two print statements print(result.value) and print(_useraSquads) don't execute until after the Future's connection state is done:
I/flutter (29217): 2
I/flutter (29217): 4
I/flutter (29217): 5
I/flutter (29217): 7
I/flutter (29217): itter
I/flutter (29217): 8
I/flutter (29217): 9
I/flutter (29217): 6
I/flutter (29217): ConnectionState.done
I/flutter (29217): {squadName: SAFTS}
I/flutter (29217): [{squadName: SAFTS}]
It seems like the problem is in the _test() function, but I've tried a hundred different ways to write this, and I can't figure out how to make sure that the code is done fetching the data from the database in the forEach block before the Future is set to done.
Your _userSquadIds's foreach is creating issue. If you want to make it async the you can use Future.forEach.
Change following code.
_userSquadIds.forEach((key, value) async {
print('itter');
final result = await _squadsRef.child(key).once();
_userSquads.add(result.value);
print(result.value);
print(_userSquads);
});
With Following one.
await Future.forEach(_userSquadIds, (key,value) async {
print('itter');
final result = await _squadsRef.child(key).once();
_userSquads.add(result.value);
print(result.value);
print(_userSquads);
});

"setState() or markNeedsBuild() called during build" error trying to push a replacement in Navigator inside a Consumer widget (provider package)

This week I've began developing in flutter and i'm not able to solve this problem.
I'm building a login page that calls an API to login and after redirects to an homepage.
This is the exception generated by Navigator.pushReplacement in the first code bloc.
In that moment apiCall.isFetching is false cause fetching ended and apiCall.response contains the required data.
Exception details:
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following assertion was thrown building Consumer<ApiCallChangeNotifier>(dirty, dependencies: [InheritedProvider<ApiCallChangeNotifier>]):
setState() or markNeedsBuild() called during build.
This Overlay widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: Overlay-[LabeledGlobalKey<OverlayState>#4dc85]
state: OverlayState#bd97e(tickers: tracking 1 ticker, entries: [OverlayEntry#2941b(opaque: false; maintainState: false), OverlayEntry#37814(opaque: false; maintainState: true), OverlayEntry#f92c0(opaque: false; maintainState: false), OverlayEntry#da26d(opaque: false; maintainState: true)])
The widget which was currently being built when the offending call was made was: Consumer<ApiCallChangeNotifier>
dirty
dependencies: [InheritedProvider<ApiCallChangeNotifier>]
User-created ancestor of the error-causing widget was:
Expanded file:///C:/flutter_test/lib/screens/login/LoginScreen.dart:153:37
When the exception was thrown, this was the stack:
#0 Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:3687:11)
#1 Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:3702:6)
#2 State.setState (package:flutter/src/widgets/framework.dart:1161:14)
#3 OverlayState.insertAll (package:flutter/src/widgets/overlay.dart:346:5)
#4 OverlayRoute.install (package:flutter/src/widgets/routes.dart:43:24)
...
Here is my function to create the login button, it's called from build function of LoginScreen (StatelessWidget)
Widget loginButton(BuildContext context) {
return Consumer<ApiCallChangeNotifier>(
builder: (context, apiCall, child) => apiCall.isFetching
? CircularProgressIndicator()
: apiCall.response != null
? Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => HomeScreen(
(apiCall.response as LoginResponse).email)))
: RaisedButton(
...
onPressed: () {
attemptLogin(context);
},
...
));
}
The attemptLogin funtion:
void attemptLogin(BuildContext context) {
Provider.of<ApiCallChangeNotifier>(context, listen: false).callApi(
MyApiServices().attemptLogin,
{
'email': emailController.value.text,
'password': passwordController.value.text,
},
urlController.value.text
);
}
ApiCallChangeNotifier
class ApiCallChangeNotifier extends ChangeNotifier {
bool isFetching = false;
Object response;
Future<LoginResponse> callApi(apiFunction, bodyParams, customUrl) async {
isFetching = true;
notifyListeners();
response = await apiFunction(bodyParams, customUrl);
isFetching = false;
notifyListeners();
return response;
}
}
MyApiServices.attemptLogin is a function that handles the API call and returns an Object LoginResponse
Hope I've given enough info!
For me, It was when I use navigator before the build finished!
Just put your navigation code in here:
WidgetsBinding.instance.addPostFrameCallback((_) {
// Do everything you want here...
});
Instead of trying to push the new route from LoginResponse Consumer I modified attemptLogin() to wait the result and to navigate to the new route!
void attemptLogin(BuildContext context) async {
LoginResponse _apiResponse =
await Provider.of<ApiCallChangeNotifier>(context, listen: false)
.callApi(
MyApiServices().attemptLogin,
{
'email': emailController.value.text,
'password': passwordController.value.text,
},
urlController.value.text);
if (_apiResponse != null) {
if (_apiResponse.email != null) {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => HomeScreen(_apiResponse.email)));
} else if (_apiResponse.errorMessage != null) {
Scaffold.of(context)
.showSnackBar(SnackBar(content: Text(_apiResponse.errorMessage)));
} else {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text(KanbanBOXApi().unknownErrorMessage)));
}
}
}