I am going to create a JSON data in my flutter application and allow users to choice what item that theirs favorite to. This is the class from Doa, and the data i take it from local JSON file.
import 'dart:convert';
List<Doa> doaFromJson(String str) =>
List<Doa>.from(json.decode(str).map((x) => Doa.fromJson(x)));
String doaToJson(List<Doa> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Doa {
Doa({
this.id,
this.grup,
this.judul,
this.lafaz,
this.latin,
this.arti,
this.tentang,
this.mood,
this.tag,
this.fav,
});
final int id;
final String grup;
final String judul;
final String lafaz;
final String latin;
final String arti;
final String tentang;
final String mood;
final String tag;
bool fav;
factory Doa.fromJson(Map<String, dynamic> json) => Doa(
id: json["id"],
grup: json["grup"],
judul: json["judul"],
lafaz: json["lafaz"],
latin: json["latin"],
arti: json["arti"],
tentang: json["tentang"],
mood: json["mood"],
tag: json["tag"],
fav: json["fav"],
);
Map<String, dynamic> toJson() => {
"id": id,
"grup": grup,
"judul": judul,
"lafaz": lafaz,
"latin": latin,
"arti": arti,
"tentang": tentang,
"mood": mood,
"tag": tag,
"fav": fav,
};
}
And this is my main page that show the list of the JSON data.
import 'package:flutter/material.dart';
import 'package:json_test/class/doa.dart';
import 'package:json_test/page/DoaPage.dart';
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
List<Doa> doaList;
bool _isInit = true;
Future<void> fetchDoa(BuildContext context) async {
final jsonstring =
await DefaultAssetBundle.of(context).loadString('assets/doa.json');
doaList = doaFromJson(jsonstring);
_isInit = false;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("JSON Data test"),
),
body: Container(
child: FutureBuilder(
future: _isInit ? fetchDoa(context) : Future(null),
builder: (context, _) {
if (doaList.isNotEmpty) {
return ListView.builder(
itemCount: doaList.length,
itemBuilder: (BuildContext context, int index) {
Doa doa = doaList[index];
return Card(
margin: EdgeInsets.all(8),
child: ListTile(
title: Text(doa.judul),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) =>
DoaPage(
doa: doa,
)));
},
trailing: IconButton(
icon: Icon(
doa.fav
? Icons.favorite
: Icons.favorite_border,
color: doa.fav ? Colors.red : null,
),
onPressed: () =>
setState(() => doa.fav = !doa.fav),
)));
},
);
}
return CircularProgressIndicator();
})));
}
}
The favorite button is worked. But, when I close the application, all of favorited items will be lost.
The result from my code shown here
After I give some 'love' for the items, when I close the app and re-open it, all of favorited items will lost. Anyone can give me some advice for my code? Thank you very much.
You should save the favorite item local phone or you can use apı service. you don't save the that item and when you close the application that item is a coming null
You can use this package for the save favorite item
shared_preferences
or
hive
Related
I am developing an android application with flutter, what I am doing is displaying a json in an application page. When I run the application it doesn't give me an error but it doesn't show me the data, the json I want to read is the following:
[
{
"deviceid": 27,
"f2cuid": "Estacion1_A",
"devicedata": {
"isRunning": 0,
"color": "w3-red",
"colorNoW3": "red",
"device_name": "Estacion1_A"
}
},
{
"deviceid": 20,
"f2cuid": "B19",
"devicedata": {
"isRunning": 1,
"color": "w3-green",
"colorNoW3": "green",
"device_name": "B19"
}
}
]
It's in my model class:
class Stations {
Int? isRunning;
String? color;
String? colorNoW3;
String? devicename;
Stations(
{this.isRunning,
this.color,
this.colorNoW3,
this.devicename,
});
factory Stations.fromJson(Map<String, dynamic> json) {
return Stations(
isRunning: json['isRunning'],
color: json['color'],
colorNoW3: json['colorNoW3'],
devicename: json['device_name'],
);
}
}
This is my service:
Future<List<Stations>> getStations() async {
Uri url = Uri.parse('URL');
final response = await http.get(url);
var data = jsonDecode(response.body);
print('data: $data');
List<Stations> stations = data.map((e) => Stations.fromJson(e)).toList();
return stations;
}
and this is the way I display it:
return Scaffold(
appBar: AppBar(
title: const Text('Sistema Escolar Administrativo'),
),
drawer: DrawerWidgetMenu(),
body: Container(
child: FutureBuilder(
future: stationSvc.getStations(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Container(
child: Center(
child: Text('No hay datos que mostrar'),
),
);
}
return snapshot.data.length > 0
? ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: InkWell(
onTap: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => HomePage(),
),
);
},
child: ListTile(
leading: Text(snapshot.data[index].devicename!),
title: Text(snapshot.data[index].color!),
subtitle: Text(snapshot.data[index].colorNoW3!),
),
));
})
: Center(
child: Text('No hay datos, registra un grupo primero'));
}),
),
);
You forgot to specify nested map:
factory Stations.fromJson(Map<String, dynamic> json) {
return Stations(
isRunning: json['devicedata']?['isRunning'],
color: json['devicedata']?['color'],
colorNoW3: json['devicedata']?['colorNoW3'],
devicename: json['devicedata']?['device_name'],
);
}
I am sharing a complete class named SampleModel below which can help to parse JSON in flutter:
class SampleModel {
String? _myName;
bool? _isActive;
SampleModel({String? myName, bool? isActive}) {
if (myName != null) {
_myName = myName;
}
if (isActive != null) {
_isActive = isActive;
}
}
String? get myName => _myName;
set myName(String? myName) => _myName = myName;
bool? get isActive => _isActive;
set isActive(bool? isActive) => _isActive = isActive;
SampleModel.fromJson(Map<String, dynamic> json) {
_myName = json['myName'];
_isActive = json['isActive'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['myName'] = _myName;
data['isActive'] = _isActive;
return data;
}
}
Sample JSON for the same class:
{
"myName" : "Your Name",
"isActive" : true
}
Check if this can help in your case.
your json type is an array, not a map.
look at [ ] syntax on json file.
to deal with an Array, you should make it to a list first:
List<Map<String,dynamic>> mylistofMapformJson = json.decode(receivedJson);
//you should get your list stations like this:
List<Stations> listStationsFromJson = List.generate(
mylistofMapformJson.length,
(index) => Stations.fromJson(mylistofMapformJson));
My problem is this. I created cubit for 2 different pages. When I am on the first page, I can fill the list inside the 2nd page and I can read it from the log. However, when I go to the second page, the list I filled in from the previous page is still empty.
Main.dart
home:
MultiBlocProvider(
providers: [
BlocProvider(create: (_) => HomeCubit(PhotoService())),
BlocProvider(create: (_)=> FavoritesCubit())//Gerekiyor,homeviewda içerisindeki methoda erişmem gerekiyor
],
child: const HomeView())
HomeView.dart
where I run the function in favoritescubit and add it to the list
onTap: () {
BlocProvider.of<FavoritesCubit>(context).addFavorite(
context,
state.selectItem![index],
);
print(state.selectItem?[index].isSelected);
context.read<FavoritesCubit>().getAllFavorites();
// print("UI --- ${state.selectItem![index].isSelected}");
// context.read<FavoriteBloc>().add(
// AddFavorite(photoList, photoList.isSelected));
// print(" ispressed ${photoList.isSelected}");
},
FavoritesCubit.dart
class FavoritesCubit extends Cubit<FavoritesState> {
FavoritesCubit() : super(const FavoritesState());
final List<PhotoModel> favoriteList = <PhotoModel>[];
Future<void> getAllFavorites() async {
print("FavoriteList : ${favoriteList.length}");
emit(state.copyWith(favoriteList: favoriteList));
}
Future<void> addFavorite(
BuildContext context,
PhotoModel photo,
) async {
photo.isSelected = !photo.isSelected;
if (favoriteList.contains(photo) == false) {
favoriteList.add(photo);
emit(state.copyWith(
favoriteList: favoriteList, isFavorite: photo.isSelected));
print("${state.favoriteList!.length}asdasd");
} else if (favoriteList.contains(photo) == true) {
favoriteList.remove(photo);
emit(state.copyWith(
favoriteList: favoriteList, isFavorite: photo.isSelected));
print("${state.favoriteList!.length}asdasd");
}
FavoriteView.dart
class FavoriteView extends StatefulWidget {
const FavoriteView({Key? key}) : super(key: key);
#override
State<FavoriteView> createState() => _FavoriteViewState();
}
class _FavoriteViewState extends State<FavoriteView> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => FavoritesCubit()..getAllFavorites(),
child: Scaffold(
appBar: AppBar(
title: const Text("Bloc Example"),
),
body: buildFavoriteList(context),
),
);
}
}
Widget buildFavoriteList(BuildContext context) {
return BlocConsumer<FavoritesCubit, FavoritesState>(
listener: (context, state) {
// TODO: implement listener
},
builder: (context, state) {
return ListView.builder(
itemCount: state.favoriteList?.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: (() {
// navigateToPostDetailPage(context, photos[index]);
}),
child: Padding(
padding: const EdgeInsets.all(10),
child: PhotoListTile(
isPressed: state.favoriteList![index].isSelected,
imageUrl: state.favoriteList![index].thumbnailUrl.toString(),
title: state.favoriteList![index].title.toString(),
url: state.favoriteList![index].url.toString(),
onTap: () {
// context.read<HomeCubit>().addFavorite(
// context,
// state.favoriteList![index],
// state.favoriteList![index].isSelected);
// context
// .read<FavoriteBloc>()
// .add(RemoveFavorite(photos[index]));
},
),
),
);
});
},
);
Okey, now with your added information about your FavoriteView it is clear what the problem is.
In your FavoriteView you create a new cubit, which is not the same as you created in the MultiBlocProvider. That is why it is always empty on your FavoriteView
create: (context) => FavoritesCubit()..getAllFavorites(), // This is the issue
Make sure that your FavoriteView is a child somewhere under your MultiBlocProvider and remove the creation of a new FavoritesCubit in that view. I.e. remove the BlocProvider in your FavoriteView
I am trying to retrieve data from a Realtime database in Firebase to Flutter. The data should be parsed to be used in the building of a listview inside a future builder. However, after I execute the code I got an error that displayed on the Emulator screen. My understanding is that there is a type mismatch inside the code of firebaseCalls method. Below is my code Main.dart, data model, Firebase data, and Error Message. Any help to figure out the issue is appreciated. Thanks in advance!
Main.dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'datamodel.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:firebase_core/firebase_core.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final ref = FirebaseDatabase.instance.ref();
Future<List<Menu>> firebaseCalls(DatabaseReference ref) async {
DataSnapshot dataSnapshot = await ref.child('Task').get();
String? jsondata =dataSnapshot.value as String?; // just in case String is not working
//String jsondata = dataSnapshot.children;// value;//[0]['Task'];// should be dataSnapshot.value
// Decode Json as a list
final list = json.decode(jsondata!);// as List<dynamic>;
return list.map((e) => Menu.fromJson(e)).toList();
}
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
//drawer: _drawer(data),
appBar: AppBar(
title: const Text('الصف السادس العلمي'),
),
body: FutureBuilder(
future: firebaseCalls(ref), // async work
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return new Text('Press button to start');
case ConnectionState.waiting:
return new Text('Loading....');
default:
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
else
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) =>
_buildTiles(snapshot.data[index]),
);
}
} // builder
)
)
);
}
////////////////////////////////////////////
Widget _buildTiles(Menu list) {
if (list.subMenu?.length == 0)
return new ListTile(
leading: Icon(list.icon),
title: Text(
list.name!,
style: TextStyle(
fontSize: list.font?.toDouble(), fontWeight: FontWeight.bold),
),
onTap: () => debugPrint("I was clicked"),
);
return new ExpansionTile(
leading: Icon(list.icon),
title: Text(
list.name!,
style: TextStyle(
fontSize: list.font?.toDouble(), fontWeight: FontWeight.bold),
),
children: list.subMenu!.map(_buildTiles).toList(),
);
}//_buildTiles
}
datamodel.dart
import 'package:flutter/material.dart';
class Menu {
String? name; // I added ?
IconData? icon;// I added ?
int? font;// I added ?
List<Menu>? subMenu= [];// I added ?
Menu({this.name, this.subMenu, this.icon,this.font});
Menu.fromJson(Map<String, dynamic> json) {
name = json['name'];
font = json['font'];
icon = json['icon'];
if (json['subMenu'] != null) {
//subMenu?.clear(); // I added ? it also recomand using !
json['subMenu'].forEach((v) {
//subMenu?.add(new Menu.fromJson(v));
subMenu?.add(Menu.fromJson(v));
});
}
}
}
Database:
Error message:
The problem is here:
DataSnapshot dataSnapshot = await ref.child('Task').get();
String? jsondata =dataSnapshot.value as String?;
If we look at the screenshot of the database you shared, it's clear that the value under the /Task path is not a string. It is in fact an entire object structure, which means you get back a Map<String, Object> from dataSnapshot.value. And since that's not a String, you get the error that you get.
The proper way to get the value of the entire Task node is with something like:
Map values = dataSnapshot.value;
And then you can get for example the name with:
print(values["name"]);
Alternatively you get get the child snapshot, and only then get the string value from it, with something like:
String? name = dataSnapshot.child("name")?.value as String?;
This problem is about how to keep the favorited item in the List, even after I re-open the app.
I want to keep the favorited item in doaList into favDoa, even after I close my app and re-open it. I've seen about the shared_preference package in flutter to store data, but i confused how can i implement it into my app. Here is my code :
import 'package:flutter/material.dart';
import 'package:json_test/class/doa.dart';
import 'package:json_test/page/DoaPage.dart';
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
List<Doa> doaList;
List<Doa> favDoa;
bool _isInit = true;
Future<void> fetchDoa(BuildContext context) async {
final jsonstring =
await DefaultAssetBundle.of(context).loadString('assets/doa.json');
doaList = doaFromJson(jsonstring);
_isInit = false;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("JSON Data test"),
),
body: Container(
child: FutureBuilder(
future: _isInit ? fetchDoa(context) : Future(null),
builder: (context, _) {
if (doaList.isNotEmpty) {
return ListView.builder(
itemCount: doaList.length,
itemBuilder: (BuildContext context, int index) {
Doa doa = doaList[index];
return Card(
margin: EdgeInsets.all(8),
child: ListTile(
title: Text(doa.judul),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) =>
DoaPage(
doa: doa,
)));
},
trailing: IconButton(
icon: Icon(
doa.fav
? Icons.favorite
: Icons.favorite_border,
color: doa.fav ? Colors.red : null,
),
onPressed: () => setState(() {
doa.fav = !doa.fav;
}),
)));
},
);
}
return CircularProgressIndicator();
})));
}
}
when I click the favorite icon in the list, it will be marked true in the "doa.fav". How can I implement the shared_preference package in my code to keep the doa.fav's data? Thank you so much for your answer :)
Try this:
Create a list of integer to store the Doa Ids, say you name it favoriteList
Each time you click the favorite button, add the Doa's id to favoriteList. Also save it to shared_preferences. It only support list of string, so you need to convert it first, something like:
List<String> stringFavoriteIds =
favoriteList.map((e) => e.toString()).toList();
SharedPrefs().favoriteIds = stringFavoriteIds ;
Next, each time you open the app, load SharedPrefs().favoriteIds to favoriteList
Compare the Doa Ids in favoriteList to your list of doa to mark Doa.fav to true for matching Ids.
I want to fetch all the infinite list view data in parts, means Initially it load 10 data item and on scroll more it should fetch next 10 items.
I am fetching data from my Laravel api and in my Laravel api endpoint there is not option for per_page, so Please help me to integrate load more option of list view data.
Here is my List data.
List<Category> _categoryList = List<Category>();
CategoryService _categoryService = CategoryService();
bool isLoading = true;
#override
void initState() {
super.initState();
_getAllCategories();
}
_getAllCategories() async {
var categories = await _categoryService.getCategories();
var result = json.decode(categories.body);
result['data'].forEach((data) {
var model = Category();
model.id = data["id"];
model.name = data["categoryName"];
model.icon = data["categoryIcon"];
setState(() {
_categoryList.add(model);
isLoading = false;
});
});
}
And I am fetching all data in simple ListTile.
child: ListView.builder(
itemCount: //_categoryList.length,
itemBuilder: (context, index) {
return ListTile(
title: _categoryList.name
);
},
),
So I have a trick and I am using it in my project. What we need here is to load more data when we are at the end of the list. So to do that we can use the ListView.builder() only:
child: ListView.builder(
itemCount: _categoryList.length + 1,
itemBuilder: (context, index) {
if(index == _categoryList.length){
// loadMore();
// return Loading();
}
return ListTile(
title: _categoryList.name
);
}),
So what we are doing is that we have set _categoryList.length + 1 to the itemCount. If _categoryList.length was 10 then we will have 11 items in the ListView and the index range will be 0 - 10. So inside the builder, we are checking that the index is equals to _categoryList.length which is 10. If the index is equals to the length of _categoryList.length, then we are at the end of the List and we can simply call some functions to load more data from Api or to show a loading widget.
I guess I got your question right, in this way you can simply lazy load data when user gets to the end of the List without using any third party libraries.
This process called pagination. Laravel provides a function for it check Pagination in Laravel
For example
$users = DB::table('users')->paginate(15);// 15 is limit per page
And check it too Paging lib in flutter dev.
I have created a sample to load more data using web service
You can see this example and you can implement for you json data.
https://android-pratap.blogspot.com/2018/12/flutter-infinite-listview-using.html
import 'package:akeepo/randomuser_infinitelist.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: InfiniteUsersList(),
);
}
}
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class InfiniteUsersList extends StatefulWidget {
static String tag = 'users-page';
#override
State<StatefulWidget> createState() {
return new _InfiniteUsersListState();
}
}
class _InfiniteUsersListState extends State<InfiniteUsersList> {
List<User> users = new List<User>();
ScrollController _scrollController = new ScrollController();
bool isPerformingRequest = false;
int pageNumber = 0;
#override
void initState() {
super.initState();
// Loading initial data or first request to get the data
_getMoreData();
// Loading data after scroll reaches end of the list
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
_getMoreData();
}
});
}
// to show progressbar while loading data in background
Widget _buildProgressIndicator() {
return new Padding(
padding: const EdgeInsets.all(8.0),
child: new Center(
child: new Opacity(
opacity: isPerformingRequest ? 1.0 : 0.0,
child: new CircularProgressIndicator(),
),
),
);
}
#override
void dispose() {
_scrollController.dispose();
super.dispose();
}
// Webservice request to load 20 users data using paging
Future<List<User>> _getUsers() async {
List<User> users = new List<User>();
setState(() {
pageNumber++;
});
String url =
"https://api.randomuser.me/?page=$pageNumber&results=20&seed=abc";
print(url);
var response = await http.get(url);
var jsonData = json.decode(response.body);
print(jsonData);
var usersData = jsonData["results"];
for (var user in usersData) {
User newUser = User(user["name"]["first"] + user["name"]["last"],
user["email"], user["picture"]["large"], user["phone"]);
users.add(newUser);
}
return users;
}
_getMoreData() async {
if (!isPerformingRequest) {
setState(() {
isPerformingRequest = true;
});
List<User> newEntries = await _getUsers(); //returns empty list
if (newEntries.isEmpty) {
double edge = 50.0;
double offsetFromBottom = _scrollController.position.maxScrollExtent -
_scrollController.position.pixels;
if (offsetFromBottom < edge) {
_scrollController.animateTo(
_scrollController.offset - (edge - offsetFromBottom),
duration: new Duration(milliseconds: 500),
curve: Curves.easeOut);
}
}
setState(() {
users.addAll(newEntries);
isPerformingRequest = false;
});
}
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Users',
style:
TextStyle(color: Colors.white, fontWeight: FontWeight.bold))),
body: Container(
child: ListView.builder(
shrinkWrap: true,
controller: _scrollController,
itemCount: users.length + 1,
itemBuilder: (BuildContext context, int index) {
if (index == users.length) {
return _buildProgressIndicator();
} else {
return ListTile(
onTap: () {
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) =>
UserDetailPage(users[index])));
},
title: Text(users[index].fullName),
subtitle: Text(users[index].mobileNumber),
leading: CircleAvatar(
backgroundImage: NetworkImage(users[index].imageUrl)),
);
}
})),
);
}
}
class User {
final String fullName;
final String email;
final String imageUrl;
final String mobileNumber;
User(this.fullName, this.email, this.imageUrl, this.mobileNumber);
}
// User Detail Page
class UserDetailPage extends StatelessWidget {
final User user;
UserDetailPage(this.user);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("User Details"),
),
body: Center(
child: Text(
user.fullName,
style: TextStyle(fontSize: 35.0),
),
),
);
}
}