Adding and Removing items from a list using provider flutter - flutter

I'm making favorite list which contains the user favorite journeys using provider then I will display these journeys in the favorite journeys screen.
Favorite.dart:
import 'package:flutter/material.dart';
class Favorite extends ChangeNotifier {
final Text date;
final Text time;
final Text source;
final Text destination;
final Text price;
Favorite(this.date, this.time, this.source, this.destination, this.price);
}
class Following extends ChangeNotifier {
List<Favorite> list = [];
add(favorite) {
list.add(favorite);
notifyListeners();
}
remove(favorite) {
list.remove(favorite);
notifyListeners();
}
}
journeys.dart (Which shows all journeys):
FirebaseAnimatedList(
shrinkWrap: true,
query: Consts.journeyRef.child("journeys"),
itemBuilder: (BuildContext context, DataSnapshot snapshot,
Animation animation, int index) {
try {
return Consumer<Following>(
builder:
(BuildContext context, value, child) {
return Dismissible(
key: UniqueKey(),
secondaryBackground: buildSwipeActionRight(),
background: buildSwipeActionLeft(),
child: ListView(
scrollDirection: Axis.vertical,
shrinkWrap: true,
children: <Widget>[
eachTile(
following,
Favorite(
Text(Map<String, dynamic>.from(
snapshot.value as Map)[Consts.pathDateJourney]),
Text(Map<String, dynamic>.from(
snapshot.value as Map)[Consts.pathTimeJourney]),
Text(Map<String, dynamic>.from(
snapshot.value as Map)[Consts.pathSourceCity]),
Text(Map<String, dynamic>.from(
snapshot.value as Map)[Consts.pathDestinationCity]),
Text(Map<String, dynamic>.from(
snapshot.value as Map)[Consts.pathPriceJourney]),),)
]),
onDismissed: (direction) =>
dismissItem(context, index, direction),
);
},
);
} catch (e) {
customSnackBar(context, e.toString(), 3, Colors.white24, Colors.brown, 17);
return const Text(
"We Can't show you information disabled by the Administrator");
}
}),
eachTile.dart:
ListTile eachTile(following, favorite) {
return ListTile(
leading: Column(
children: [
favorite.date,
const SizedBox(
height: 10,
),
favorite.time,
],
),
title: Row(
children: [
favorite.source,
const SizedBox(
width: 50,
),
favorite.price
],
),
subtitle: favorite.destination,
trailing: IconButton(
icon: following.list.contains(favorite)
? const Icon(Icons.favorite)
: const Icon(Icons.favorite_border_outlined),
onPressed: () {
print(following.list.contains(favorite));
// this print statement always false
if (following.list.contains(favorite)) {
following.remove(favorite);
} else {
following.add(favorite);
}
print(following.list.contains(favorite));
// this print statement always true
print(following.list);
// this print statement print the list and in each time the code execute new instance added to the list
},
),
);
}
This code is working fine as adding the journey to the list but the Problem is that when you click on the favorite icon again the condition
following.list.contains(favorite)
is returning false (Which means this object is not in the list but that's wrong I try to print the list and there is an instance) it seem that the following instance changed but I didn't create any new instance I think it is creating new different instance in each time.
What is the best way to add and remove items to the favorite list using provider?
the output:
I/flutter ( 578): false
I/flutter ( 578): true
I/flutter ( 578): [Instance of 'Favorite']
V/AutofillManager( 578): requestHideFillUi(null): anchor = null
I/flutter ( 578): false
I/flutter ( 578): true
I/flutter ( 578): [Instance of 'Favorite', Instance of 'Favorite']
V/AutofillManager( 578): requestHideFillUi(null): anchor = null
I/flutter ( 578): false
I/flutter ( 578): true
I/flutter ( 578): [Instance of 'Favorite', Instance of 'Favorite', Instance of 'Favorite']

first clean the code here in journey.dart you are using both the methods of provider you can archive this task by
1.you can use consumer widget or provider.of(context) but you are using both ways to call only Following provider
2.in journey.dart if you decided to use consumer then in (BuildContext context, Following value, Widget? child) you can use value and child directly no need to write data types in front eg.. (BuildContext context, value, child)
3.can you display your console output

The problem was that I'm making equal-to operator between two object and it's returning false that is because all objects in the dart language except the primitive data types like string, int, and double... are not equal to each other since no == operator is overridden and set to it, and this include also collections like lists, maps...
the solution is to override the == operator in Favorite class:
class Favorite{
final Text date;
final Text time;
final Text source;
final Text destination;
final Text price;
Favorite(this.date, this.time, this.source, this.destination, this.price);#override
bool operator == (Object other) {
return other is Favorite && date.toString() == other.date.toString() && time.toString() == other.time.toString() && source.toString() == other.source.toString() && destination.toString() == other.destination.toString() && price.toString() == other.price.toString();
}
}
now when I run following.list.contains(favorite) or following.list[0]==favorite in listTile Widget it will return true.
and that's it

Every time you make a change, you should call notifyListeners();. An example Implementation would be:
Inside your Provider Class:
void add(int n) {
myList.add(n);
notifyListeners();
}

Related

How Should I Deal with Null Values in a StreamBuilder?

I'm trying to build a flutter view that loads a list of items ('cost codes' in the code snippet) from a database call. This code works elsewhere in my project where I already have data in the database, but it fails when it tries to read data from an empty node. I can provide dummy data or sample data for my users on first run, but they might delete the data before adding their own, which would cause the app to crash the next time this view loads.
What's the proper way to deal with a potentially empty list in a StreamBuilder?
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: dbPathRef.onValue,
builder: (context, snapshot) {
final costCodes = <CostCode>[];
if (!snapshot.hasData) {
return Center(
child: Column(
children: const [
Text(
'No Data',
style: TextStyle(
color: Colors.white,
),
)
],
),
);
} else {
final costCodeData =
// code fails on the following line with the error
// 'type "Null" is not a subtype of type "Map<Object?, dynamic>" in type cast'
(snapshot.data!).snapshot.value as Map<Object?, dynamic>;
costCodeData.forEach(
(key, value) {
final dataLast = Map<String, dynamic>.from(value);
final account = CostCode(
id: dataLast['id'],
name: dataLast['name'],
);
costCodes.add(account);
},
);
return ListView.builder(
shrinkWrap: false,
itemCount: costCodes.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(
costCodes[index].name,
style: const TextStyle(color: Colors.white),
),
subtitle: Text(
costCodes[index].id,
style: const TextStyle(color: Colors.white),
),
);
},
);
}
},
);
}
Personally I tend to avoid handling raw data from a database in the UI code and handle all of this in a repository/bloc layer.
However, to solve your issue you can simply add a ? to the end of the cast like so:
final costCodeData = (snapshot.data!).snapshot.value as Map<Object?, dynamic>?;
You will no longer get the cast exception - however you still have to test costCodeData for null.
This block of code may help:
final data = snapshot.data;
final Map<Object?, dynamic>? costCodeData
if (data == null) {
costCodeData = null;
} else {
costCodeData = (snapshot.data!).snapshot.value as Map<Object?, dynamic>?;
}
if (costCodeData == null){
// Show noData
} else {
// Show data
}
final dataLast = Map<String, dynamic>.from(value);
final account = CostCode(
id: dataLast['id'],
name: dataLast['name'],
);
costCodes.add(account);
},
you declaired dataLast with a Map having key as String, but inside the account variable the id and name are not in the string format, keep those inside "" || '' even after modiying these, if you still face other issue try putting question mark at the end of the line
(snapshot.data!).snapshot.value as Map<Object, dynamic>?

Flutter Riverpod StateNotifier initialize state is empty but whenever buildMethod rebuild it's not empty

I got a HiveBox and I want to access it with Riverpod StateNotifier.
This is how I defined this provider:
final hiveSalonProvider =
StateNotifierProvider<HiveSalonNotifier, List>((ref) {
return HiveSalonNotifier();
});
Then i created a StateNotifier class which it's listening to list of SalonModel class.
class HiveSalonNotifier extends StateNotifier<List<SalonModel>> {
HiveSalonNotifier([List<SalonModel>? state])
: super(state ?? <SalonModel>[]) {
_cacheManager = SalonCacheManager('boxB');
fetchDatasFromHiveBox();
}
late final CacheManagerBase<SalonModel> _cacheManager;
List<SalonModel>? salonItems = [];
Future<void> fetchDatasFromHiveBox() async {
await _cacheManager.init();
if (_cacheManager.getValues()?.isNotEmpty ?? false) {
state = _cacheManager.getValues()!;
salonItems?.addAll(state);
print('provider: datas from caches');
} else {
print('provider:provider: datas from services');
}
}
It seems there is no error. I think so there is not.
But in UI (StatelessWidget);
In build method, I have defined our provider:
var hive = ref.read(hiveSalonProvider.notifier);
In Column:
(hive.salonItems?.isNotEmpty ?? false)
? ListView.builder(
shrinkWrap: true,
itemCount: hive.salonItems?.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: const CircleAvatar(),
title: Text(
'${hive.salonItems?[index].salonName.toString()}'),
);
},
)
: const CircularProgressIndicator(color: Colors.white),
At first hot reload, this widget showing me CircularProgressIndicator. But then I press the save code combination from keyboard (CTRL+S in vscode), it's showing listView correctly.
Where is the problem ?

The getter 'length' was called on null. Receiver: null. How to solve this error for the list of lists?

i have got list of lists and I need to retrieve data from them. I am using Provider for fetching data from the API. So I screated the ExpansionTile with 2 Listview.builder, because I have read that for retrieving data I need to use some loop for each list, e.g Listview.builder. But now it gives me the error
"The getter 'length' was called on null.
Receiver: null
Tried calling: length"
But when I use print the array isn't null, so I don't get it why I getting this error.
My code is:
class _StopScreensState extends State<StopScreens> {
List<Stop> stop;
List<Routes> routes;
List <Arrival> arrivals;
#override
void didChangeDependencies() {
stop = Provider.of<List<Stop>>(context).toList();
routes = Provider.of<List<Routes>>(context).toList();
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
//The list of lists
Iterable<Common> merge(List<Arrival> arrivals, List<Stop> stop,
List<Routes> route) sync* {
for (int i = 0; i < arrivals.length; i++) {
var fishingTackle = routes.where((v) => v.mrId == arrivals[i].mrId).toList();
var fishBait = stop.where((v) => v.stId == arrivals[i].stId).toList();
yield Common(
id: arrivals[i].mrId,
typeId: arrivals[i].mrId,
fishingTackle: fishingTackle,
fishBait: fishBait,
time: arrivals[i].taArrivetime,
);
}
}
//the common list for all arrays
List<Common> common = merge(arrivals, stop, routes).toList();
return Scaffold(
appBar: AppBar(
title: Text('Остановки'),
),
body: Provider.value(value: common) == null
? Center(
child: CircularProgressIndicator(),
)
: ListView.builder(
itemCount: common.length,
itemBuilder: (context, index) {
return ListView.builder(
itemBuilder: (BuildContext context, int index) {
return ExpansionTile(title: Text(common[index].fishingTackle[index].mrTitle),
children: [
ListView.builder(itemCount: stop.length,itemBuilder: (context, index){
return ListTile(
title: Text(common[index].fishBait[index].stTitle),
leading: Text(common[index].time.toString()),
);
It's because you have a null list.
Try to always initialized your list with an empty list, so you don't need to handle the null value for each list.
Change:
List<Stop> stop;
List<Routes> routes;
List <Arrival> arrivals;
to
List<Stop> stop = [];
List<Routes> routes = [];
List <Arrival> arrivals = [];
It seems you're not assigning anything to arrivals

How to fix the getter length was called on null in Flutter

I'm getting the NoSuchMethodError: The gettter 'length' was called on null so just wondering how to fix this issue.
The issue happend when I try to get the length of the favorite value.
Favorite View Model
class FavoriteViewModel extends BaseViewModel {
List<FavoriteModel> favorites = [];
void initialize(FavoriteService favProvider) {
favorites = favProvider.getFavorites();
}
}
Reorder Screen
class _ReorderPageState extends State<ReorderPage> {
#override
Widget build(BuildContext context) {
var favProvider = Provider.of<FavoriteService>(context, listen: true);
return BaseView<FavoriteViewModel>(onModelReady: (model) {
model.initialize(favProvider);
}, builder: (context, model, child) {
return model.state == ViewState.Busy
......
Widget reorderWidget(FavoriteViewModel model, BuildContext bcontext) {
return Theme(
data: ThemeData(primaryColor: Colors.transparent),
child: ReorderableListView(
onReorder: (int oldIndex, int newIndex) {
_onParentReorder(oldIndex, newIndex, model);
},
scrollDirection: Axis.vertical,
children: List.generate(
model.favorites.length, // I think the issue is in this line
(index) {
FavoriteModel favorite = model.favorites[index]; // I think the issue is in this line
Did you already try to use elvis operator (similar to typescript and kotlin) ?
model?.favorites?.length
and also, its possible in your viewModel initializer favProvider.getFavorites() is always null ??

Flutter Application showing blank screen when trying to invoke a Future Builder

I am trying to build a futureBuilder with a Future Function that I have which passes through a list of objects which contain the surveyName parameter, I am trying to display that surveyName descriptor into a listView but am noticing that I am getting a blank white screen that isn't showing both the container which is just supposed to show some basic loading functionality and it isn't displaying the information of the getSurveys function either.
I am new to both dart and flutter, so this may just have some basic simple resolution but any information would be helpful. The method getSurveys below in the print displays both names in the print so I know the information is coming in correct for that specific function but I am wondering why it isn't working within the futureBuilder.
The 1 and 2 print statements are running but I am noticing the 3 print statement isn't so that listView Builder is not being invoked for some starnge reason, which may be the cause of this dilemma but I wonder why the container which is just supposed to showcase a loading... is not working correctly either. Output is below this function.
Future<List<Survey_List>> getSurveys() async{
Map map = {
'Type': "getSurveys"
};
var _list = [];
List<Survey_List> surveys = [];
getPost(map).then((Map value){
_list.addAll(value["survey"]);
for (int i = 0; i < _list.length; i++){
Survey_List survey_list = Survey_List(surveyName: _list[i].toString() ,surveyDescription: "", surveyVersion: "");
surveys.add(survey_list);
print(survey_list.surveyName);
}
});
return surveys;
}
Output is as follows:
I/flutter ( 2020): 2
I/flutter ( 2020): 1
I/flutter ( 2020): {"survey":["Survey","Surve3ww"]}
I/flutter ( 2020): Survey
I/flutter ( 2020): Surve3ww
import 'dart:convert';
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter_application/Http.dart';
import 'Config.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class Survey_List extends StatefulWidget {
String surveyName;
String surveyDescription;
String surveyVersion;
Survey_List({
this.surveyName,
this.surveyDescription,
this.surveyVersion,
Key key,
}) : super (key: key);
#override
_SurveyListState createState() => _SurveyListState();
}
class _SurveyListState extends State<Survey_List> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Surveys"),
),
body: new Container(
child: new FutureBuilder <List<Survey_List>>(
future: getSurveys(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
List<Survey_List> surveys = snapshot.data;
if (snapshot.hasData) {
print(1);
return new ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
print(3);
Survey_List survey_list = surveys[index];
print(survey_list.toString());
return new ListTile(
title: new Text(survey_list.surveyName)
);
},
);
}
else{
print(2);
return new Container(
child: new Center(
child: new Text("Loading...")
)
);
}
},
),
),
);
}
}