Hello everyone here's my test app and I have some problems with making a Favorite page section where you can tap on button and add the item into fav page.
I'm receiving a data from API and implementing it by Listview.builder
Here are some photos of how it should look like:
Home page
Favorite page
main.dart, here I'm openning a box called 'favorites_box'
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
void main() async{
await GetStorage.init();
await Hive.openBox('favorites_box');
runApp(MainPage());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GetMaterialApp(
initialRoute: '/',
getPages: [
GetPage(name: '/', page: () => MyApp()),
GetPage(name: '/main-page', page: () => MainPage()),
GetPage(name: '/favorite_page', page: () => FavoritePage()),
// Dynamic route
],
home: MainPage(),
);
}
}
Well here's a code of home page:
main_page.dart
import 'package:flutter/material.dart';
import '../View/listview_api.dart';
class MainPage extends StatefulWidget {
#override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
int currentIndex = 0;
List<BottomNavigationBarItem>? items;
final screens = [
HomePage(),
HomePage()
FavoritePage(),
HomePage()
];
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: SafeArea(
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: Container(
width: double.infinity,
height: 40,
color: Colors.white,
child: Center(
child: TextField(
decoration: InputDecoration(
border: OutlineInputBorder(
),
hintText: 'Searching',
prefixIcon: Icon(Icons.search),
suffixIcon: Icon(Icons.notifications)),
),
),
),
),
body: screens[currentIndex],
bottomNavigationBar: BottomNavigationBar(
unselectedItemColor: Colors.grey,//AppColors.unselectedBottomNavItem,
selectedItemColor: Colors.blue,//AppColors.assets,
onTap: (index) => setState(() {
currentIndex = index;
}),//controller.setMenu(BottomMenu.values[pos]),
//currentIndex: ,//controller.bottomMenu.index,
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.white,
currentIndex: currentIndex,
selectedLabelStyle: const TextStyle(
fontSize: 10,
fontWeight: FontWeight.w500,
),
unselectedLabelStyle: const TextStyle(
fontSize: 10,
fontWeight: FontWeight.w500,
),
elevation: 8,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
backgroundColor: Colors.blue,
),
BottomNavigationBarItem(
icon: Icon(Icons.add_shopping_cart),
label: 'Shopping cart',
backgroundColor: Colors.red,
),
BottomNavigationBarItem(
icon: Icon(Icons.favorite),
label: 'Favorite',
backgroundColor: Colors.green,
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profile',
backgroundColor: Colors.yellow,
),
],
),
),
),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SafeArea(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Center(
child: Padding(
padding: EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
//Image.asset('images/image0.jpg'),
SizedBox(
height: 25.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'New!',
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 25.0,
fontWeight: FontWeight.bold,
),
),
IconButton(
onPressed: () {},
icon: Icon(
Icons.arrow_forward_outlined,
),
),
],
),
SizedBox(
height: 25.0,
),
SizedBox(
height: 300.0,
width: double.infinity,
child: ListViewAPI(),
),
],
),
),
),
),
);
}
}
And now, below is a code of ListViewAPI(), here I've added the elements which I tap to the box('favorites_box'): listview_api.dart
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
String? stringResponse;
Map? mapResponse;
Map? dataResponse;
List? listResponse;
class ListViewAPI extends StatefulWidget {
const ListViewAPI({Key? key}) : super(key: key);
#override
_ListViewAPIState createState() => _ListViewAPIState();
}
class _ListViewAPIState extends State<ListViewAPI> {
Future apiCall() async {
http.Response response;
response = await http.get(Uri.parse("https://api.client.macbro.uz/v1/product"));
if(response.statusCode == 200) {
setState(() {
// stringResponse = response.body;
mapResponse = jsonDecode(response.body);
listResponse = mapResponse!['products'];
});
}
}
#override
void initState() {
super.initState();
apiCall();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scrollbar(
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return Stack(
children: [
Card(
child: Image.network(
listResponse![index]['image'],
),
),
Positioned(
right: 0,
child: InkWell(
child: IconButton(
onPressed: () async {
await Hive.box('favorites_box').put(listResponse![index]['image'], listResponse);
},
icon: Icon(
Icons.favorite_rounded,
color: Colors.red,
),
),
),
),
],
);
},
itemCount: listResponse == null ? 0 : listResponse!.length,
),
),
);
}
}
So here, I created a list, and tried to save the elements from box named "favorites_box" and got data which was added while I tap favorite IconButton upper but without success( :
favorite_page.dart
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
import '../View/gridview_api.dart';
class FavoritePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: ValueListenableBuilder(
valueListenable: Hive.box('favorites_box').listenable(),
builder: (context, box, child) {
List posts = List.from(Hive.box('favorites_box').values);
return ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return Column(
children: [
Text(
'List of favorite products'
),
Card(
child: posts[index] == null ? Text('nothing(') : posts[index],
// child: Hive.box('favorites_box').get(listResponse),
),
],
);
},
);
},
),
);
}
}
I'll be grateful if someone could help me with this problem, as I'm trying to fix this issue for a couple of days
P.s. I'm so sorry for some inconveniences, I'm a novice yet that's why hope you'll understand me
Thanks!
Alright. I now have a solution. It is a bit more complex than what you started with but it worked during testing.
Using https://marketplace.visualstudio.com/items?itemName=hirantha.json-to-dart I created a model class from the API data JSON. One for the Product and one for the Price map inside of Product.
product_model.dart
import 'dart:convert';
import 'package:equatable/equatable.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'price.dart';
part 'product_model.g.dart';
#HiveType(typeId: 1)
class ProductModel extends Equatable {
#HiveField(0)
final String? id;
#HiveField(1)
final String? name;
#HiveField(2)
final String? slug;
#HiveField(3)
final bool? active;
#HiveField(4)
final String? image;
#HiveField(5)
final String? code;
#HiveField(6)
final String? order;
#HiveField(7)
final int? cheapestPrice;
#HiveField(8)
final Price? price;
#HiveField(9)
final int? discount;
const ProductModel({
this.id,
this.name,
this.slug,
this.active,
this.image,
this.code,
this.order,
this.cheapestPrice,
this.price,
this.discount,
});
factory ProductModel.fromMap(Map<String, dynamic> data) => ProductModel(
id: data['id'] as String?,
name: data['name'] as String?,
slug: data['slug'] as String?,
active: data['active'] as bool?,
image: data['image'] as String?,
code: data['code'] as String?,
order: data['order'] as String?,
cheapestPrice: data['cheapest_price'] as int?,
price: data['price'] == null
? null
: Price.fromMap(data['price'] as Map<String, dynamic>),
discount: data['discount'] as int?,
);
Map<String, dynamic> toMap() => {
'id': id,
'name': name,
'slug': slug,
'active': active,
'image': image,
'code': code,
'order': order,
'cheapest_price': cheapestPrice,
'price': price?.toMap(),
'discount': discount,
};
/// `dart:convert`
///
/// Parses the string and returns the resulting Json object as [ProductModel].
factory ProductModel.fromJson(String data) {
return ProductModel.fromMap(json.decode(data) as Map<String, dynamic>);
}
/// `dart:convert`
///
/// Converts [ProductModel] to a JSON string.
String toJson() => json.encode(toMap());
ProductModel copyWith({
String? id,
String? name,
String? slug,
bool? active,
String? image,
String? code,
String? order,
int? cheapestPrice,
Price? price,
int? discount,
}) {
return ProductModel(
id: id ?? this.id,
name: name ?? this.name,
slug: slug ?? this.slug,
active: active ?? this.active,
image: image ?? this.image,
code: code ?? this.code,
order: order ?? this.order,
cheapestPrice: cheapestPrice ?? this.cheapestPrice,
price: price ?? this.price,
discount: discount ?? this.discount,
);
}
#override
bool get stringify => true;
#override
List<Object?> get props {
return [
id,
name,
slug,
active,
image,
code,
order,
cheapestPrice,
price,
discount,
];
}
}
price.dart
import 'dart:convert';
import 'package:equatable/equatable.dart';
import 'package:hive_flutter/hive_flutter.dart';
part 'price.g.dart';
#HiveType(typeId: 2)
class Price extends Equatable {
#HiveField(0)
final int? price;
#HiveField(1)
final int? oldPrice;
#HiveField(2)
final int? uzsPrice;
#HiveField(3)
final int? secondPrice;
#HiveField(4)
final int? secondUzsPrice;
const Price({
this.price,
this.oldPrice,
this.uzsPrice,
this.secondPrice,
this.secondUzsPrice,
});
factory Price.fromMap(Map<String, dynamic> data) => Price(
price: data['price'] as int?,
oldPrice: data['old_price'] as int?,
uzsPrice: data['uzs_price'] as int?,
secondPrice: data['second_price'] as int?,
secondUzsPrice: data['second_uzs_price'] as int?,
);
Map<String, dynamic> toMap() => {
'price': price,
'old_price': oldPrice,
'uzs_price': uzsPrice,
'second_price': secondPrice,
'second_uzs_price': secondUzsPrice,
};
/// `dart:convert`
///
/// Parses the string and returns the resulting Json object as [Price].
factory Price.fromJson(String data) {
return Price.fromMap(json.decode(data) as Map<String, dynamic>);
}
/// `dart:convert`
///
/// Converts [Price] to a JSON string.
String toJson() => json.encode(toMap());
Price copyWith({
int? price,
int? oldPrice,
int? uzsPrice,
int? secondPrice,
int? secondUzsPrice,
}) {
return Price(
price: price ?? this.price,
oldPrice: oldPrice ?? this.oldPrice,
uzsPrice: uzsPrice ?? this.uzsPrice,
secondPrice: secondPrice ?? this.secondPrice,
secondUzsPrice: secondUzsPrice ?? this.secondUzsPrice,
);
}
#override
bool get stringify => true;
#override
List<Object?> get props {
return [
price,
oldPrice,
uzsPrice,
secondPrice,
secondUzsPrice,
];
}
}
I then used https://docs.hivedb.dev/#/custom-objects/generate_adapter to create adapters for both of those. You can read the documentation to see how that is done using build_runner and the hive_generator packages.
In main.dart I registered both of the adapters and opened up a box with the ProductModel type from product_model.dart.
main.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:test/product_model/price.dart';
import 'package:test/product_model/product_model.dart';
import 'favorite_page.dart';
import 'homepage.dart';
void main() async {
// await GetStorage.init();
await Hive.initFlutter();
Hive.registerAdapter(PriceAdapter());
Hive.registerAdapter(ProductModelAdapter());
await Hive.openBox<ProductModel>('favorites_box');
runApp(MainPage());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GetMaterialApp(
initialRoute: '/',
getPages: [
GetPage(name: '/', page: () => MyApp()),
GetPage(name: '/main-page', page: () => MainPage()),
GetPage(name: '/favorite_page', page: () => FavoritePage()),
// Dynamic route
],
home: MainPage(),
);
}
}
listview_api.dart is mostly the same with the exception of mapping the products from listResponse to ProductModel objects.
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:test/product_model/product_model.dart';
String? stringResponse;
Map? mapResponse;
Map? dataResponse;
List? listResponse;
class ListViewAPI extends StatefulWidget {
const ListViewAPI({Key? key}) : super(key: key);
#override
_ListViewAPIState createState() => _ListViewAPIState();
}
class _ListViewAPIState extends State<ListViewAPI> {
Future apiCall() async {
http.Response response;
response =
await http.get(Uri.parse("https://api.client.macbro.uz/v1/product"));
if (response.statusCode == 200) {
setState(() {
// stringResponse = response.body;
mapResponse = jsonDecode(response.body);
listResponse = mapResponse!['products'];
listResponse =
listResponse!.map((e) => ProductModel.fromMap(e)).toList(); // Map all of the products in listResponse to a ProductModel object.
});
}
}
#override
void initState() {
super.initState();
apiCall();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scrollbar(
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return Stack(
children: [
Card(
child: Image.network(
listResponse![index].image!,
),
),
Positioned(
right: 0,
child: InkWell(
child: IconButton(
onPressed: () async {
await Hive.box<ProductModel>('favorites_box').put(
listResponse![index].image, listResponse![index]);
},
icon: Icon(
Icons.favorite_rounded,
color: Colors.red,
),
),
),
),
],
);
},
itemCount: listResponse == null ? 0 : listResponse!.length,
),
),
);
}
}
homepage.dart is unchanged.
favorite_page.dart was changed to a stateful widget and then gets the box values on init.
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:test/product_model/product_model.dart';
class FavoritePage extends StatefulWidget {
#override
State<FavoritePage> createState() => _FavoritePageState();
}
class _FavoritePageState extends State<FavoritePage> {
var posts;
#override
void initState() {
super.initState();
posts = Hive.box<ProductModel>('favorites_box').values.toList();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return Stack(
children: [
Card(
child: Image.network(
posts[index].image!,
),
),
Positioned(
right: 0,
child: InkWell(
child: IconButton(
onPressed: () async {
await Hive.box<ProductModel>('favorites_box')
.delete(posts[index]);
},
icon: Icon(
Icons.favorite_rounded,
color: Colors.red,
),
),
),
),
],
);
},
itemCount: posts == null ? 0 : posts.length,
),
);
}
}
I really encourage you to read the documentation on Hive as it contains a wealth of information. Another tip when coding with hive is to make sure you are clearing out the storage and cache for your emulator or physical device regularly. I have had too many headaches dealing with errors in Hive simply because I forgot to clear the storage and cache which was resulting in bad data despite having changed my source code.
I don't believe this is a problem with your code. However, I do recommend creating a model class for your data and maybe using a FutureBuilder https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html.
I believe the problem is that you have not updated your AndroidManifest.xml file to allow for internet connectivity.
Try adding:
<uses-permission android:name="android.permission.INTERNET" />
to your android\app\src\main\AndroidManifest.xml, above <application.
Further reading: https://flutter-examples.com/add-permissions-in-androidmanifest-xml-file/
After taking a closer look at your issue, I think I figured out the problem.
Hive requires an init:
void main() async {
// await GetStorage.init(); // Not sure why this was here but doesn't seem to be needed.
await Hive.initFlutter();
await Hive.openBox('favorites_box');
runApp(MainPage());
}
You were also missing a comma in main_page.dart
final screens = [
HomePage(),
HomePage() <----
FavoritePage(),
HomePage()
];
For your favorites page, I replaced the ValueListenableBuilder with just a ListView.builder:
class FavoritePage extends StatelessWidget {
List posts = List.from(Hive.box('favorites_box').values);
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return Stack(
children: [
Card(
child: Image.network(
posts[index]['image'],
),
),
Positioned(
right: 0,
child: InkWell(
child: IconButton(
onPressed: () async {
await Hive.box('favorites_box').delete(posts[index]);
},
icon: Icon(
Icons.favorite_rounded,
color: Colors.red,
),
),
),
),
],
);
},
itemCount: posts == null ? 0 : posts.length,
),
);
}
}
There is still an error when you try to use this that says that posts[index]['image'] type 'String' is not a subtype of type 'int' of 'index' but you can easily fix this by creating a model class and accessing everything with those properties. Using model class in flutter (dart) here is an example of a model class. Instead of using DocumentSnapshot, you can add a toList() or toMap() method.
Hope this helps. It is working on my emulator. but I am just printing out the full string instead of using the image in the Card child.
Example Model Class:
import 'dart:convert';
void main() async {
String data = '{"id":"626694d4f1ce2a0012f0fe1c","name":"JBL Party Box On-The-Go","slug":"jbl-party-box-on-the-go-juqgil2ep8ult","active":true,"image":"https://cdn.macbro.uz/macbro/1fad4f47-51f4-4f12-975b-657d780c98af","code":"","order":"0","cheapest_price":0,"price":{"price":520,"old_price":0,"uzs_price":5994000,"second_price":0,"second_uzs_price":7012500},"discount":0}';
var test = new ProductModel.fromJson(json.decode(data));
print(test.image);
}
class ProductModel {
String? name;
String? image;
ProductModel.fromJson(Map json) {
this.name = json['id'];
this.image = json['image'];
}
}
I'm a beginner in flutter, I'm building a simple Note app where users can enter, edit and save the note in local db. I'm able to save the notes to DB but it is not fetched as soon as it is saved. I need to restart the app to see the note list.
This is a Notes model class
import 'db_operations.dart';
class Note {
int id;
String title;
String body;
Note(this.id, this.title, this.body);
Note.fromMap(Map<String, dynamic> map) {
id = map['id'];
title = map['title'];
body = map['body'];
}
Map<String, dynamic> toMap(){
return {
DatabaseHelper.columnId : id,
DatabaseHelper.columnTitle : title,
DatabaseHelper.columnBody : body
};
}
#override
String toString(){
return 'Note{title : $title, body : $body}';
}
}
This is a Database helper class
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'model_notes.dart';
class DatabaseHelper {
static final _databaseName = "myNote.db";
static final _databaseVersion = 1;
static final table = 'notes_table';
static final columnId = 'id';
static final columnTitle = 'title';
static final columnBody = 'body';
DatabaseHelper._privateConstructor();
static final DatabaseHelper instance = DatabaseHelper._privateConstructor();
static Database _database;
Future<Database> get database async {
if (_database != null) return _database;
// lazily instantiate the db the first time it is accessed
_database = await initDatabase();
return _database;
}
initDatabase() async {
String path = join(await getDatabasesPath(), _databaseName);
return await openDatabase(path,
version: _databaseVersion,
onCreate: _onCreate);
}
Future _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE $table (
$columnId INTEGER PRIMARY KEY AUTOINCREMENT,
$columnTitle TEXT NOT NULL,
$columnBody TEXT NOT NULL
)
''');
}
Future<int> insert(Note note) async {
Database db = await instance.database;
if (note.title.trim().isEmpty) note.title = 'Untitled Note';
return await db.insert(table, {'title': note.title, 'body': note.body});
}
Future<List<Note>> getNotesFromDB() async {
final db = await database;
List<Note> notesList = [];
List<Map> maps = await db.query(table);
if (maps.length > 0) {
maps.forEach((map) {
notesList.add(Note.fromMap(map));
});
}
return notesList;
}
}
This is where I am adding notes
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:note_taking_app/constants/buttons_and_icons_misc(classes).dart';
import 'package:note_taking_app/db/db_operations.dart';
import 'package:note_taking_app/db/model_notes.dart';
import 'package:sqflite/sqflite.dart';
import 'main_screen.dart';
final bodyController = TextEditingController();
final headerController = TextEditingController();
final dbHelper = DatabaseHelper.instance;
class AddingNotes extends StatefulWidget {
#override
_AddingNotesState createState() => _AddingNotesState();
}
class _AddingNotesState extends State<AddingNotes> {
#override
void initState() {
super.initState();
bodyController.clear();
headerController.clear();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
backwardsCompatibility: true,
leading: LeadingIcon(
callBack: () {
Navigator.pop(context);
},
),
backgroundColor: Colors.white.withOpacity(0.4),
actions: <Widget>[
ActionsIconButton(
icon: Icon(undo, color: black),
callBack: () {
debugPrint('undo tapped');
},
),
ActionsIconButton(
icon: Icon(redo, color: black),
callBack: () {
debugPrint('redo tapped');
},
),
ActionsIconButton(
icon: Icon(save, color: black),
callBack: () async {
debugPrint(bodyController.text);
debugPrint(headerController.text);
String title = headerController.text;
String body = bodyController.text;
Note note = Note(20, title, body);
var value = await dbHelper.insert(note);
print("if 1 is return then insert success and 0 then not inserted : $value");
Navigator.pop(context);
},
)
],
),
body: Container(
color: Colors.white.withOpacity(0.4),
child: Padding(
padding: const EdgeInsets.all(13.0),
child: Column(
children: [
HeaderBody(
textEditingController: headerController,
),
SizedBox(
height: 32.0,
),
Expanded(
child: NotesBody(
textEditingController: bodyController,
),
),
],
),
),
),
);
}
}
This is where I am displaying notes
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:note_taking_app/constants/text_and_decorations(methods).dart';
import 'package:note_taking_app/db/model_notes.dart';
import 'package:note_taking_app/ui/adding_notes.dart';
import 'package:note_taking_app/db/db_operations.dart';
class MainScreen extends StatefulWidget {
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
List<Note> noteList = [];
#override
void initState() {
super.initState();
dbHelper.initDatabase();
setNotesFromDB();
}
setNotesFromDB() async{
print("Entered setNotes");
var fetchedNotes = await dbHelper.getNotesFromDB();
setState(() {
noteList = fetchedNotes;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
title: titleText,
backwardsCompatibility: false,
automaticallyImplyLeading: false,
backgroundColor: Colors.white.withOpacity(0.4),
),
floatingActionButton: Container(
height: 80.0,
width: 80.0,
child: FittedBox(
child: FloatingActionButton(
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => AddingNotes()));
},
child: const Icon(
Icons.add,
),
backgroundColor: Colors.green,
),
),
),
body: ListView.builder(
itemCount: noteList.length,
itemBuilder: (context, index){
return ListTile(
title: Text('${noteList[index]}'),
);
},
),
);
}
}
When I restart the app I'm getting user-entered notes. How can I make this dynamic, as a user enters a note and save it, it should be displayed on Main Screen. but I don't know how to do that. Any help here guys, I'm stuck at this..
The problem is that you are using a future, once it is complete there will be no updates, unless the Widget calling the getNotesFromDB() gets rebuild, which is the case when you restart the app or the emulator.
Moreover, you are also only fetching the notes in initState() of your MainScreen.
Option 1:
Try turning your ListView.builder in the body into a streambuilder, which is refreshing your Screen whenever a new note is saved.
Option 2:
Alternatively, implement a pull-to-refresh logic, which calls getNotesFromDB() for you. Checkout this video from the FlutterTeam to implement the feature.
I'm getting this error:
NosuchmethodError: Class 'List' has no instance setter 'state='.
Recevier: Instance(length:0) of ' _GrowableList' Tried calling: state=Instance(lenght:15) of'_GrowableList'
and I don't know where is the problem I can't see any problem in the code.
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:quiz2/const/const.dart';
import 'package:sqflite/sqflite.dart';
class Category {
int ID;
String name, image;
Map<String, dynamic> toMap() {
var map = <String, dynamic> {
columnMainCategoryId: ID,
columnCategoryName: name,
columnCategoryImage: image
};
return map;
}
Category();
Category.fromMap(Map<String,dynamic> map) {
ID = map[columnMainCategoryId];
name = map[columnCategoryName];
image = map[columnCategoryImage];
}
}
class CategoryProvider {
Future<Category> getCategoryById(Database db, int id)async {
var maps = await db.query(tableCategoryName,
columns: [
columnMainCategoryId,
columnCategoryName,
columnCategoryImage
], where: "$columnMainCategoryId=?",
whereArgs: [id]);
if(maps.length > 0)
return Category.fromMap(maps.first);
return null;
}
Future<List<Category>> getCategories(Database db) async{
var maps = await db.query(tableCategoryName,
columns: [
columnMainCategoryId,
columnCategoryName,
columnCategoryImage
]);
if(maps.length > 0) return maps.map((category) => Category.fromMap(category)).toList();
return null;
}
}
class CategoryList extends StateNotifier<List<Category>>{
CategoryList(List<Category> state):super(state ?? []);
void addAll(List<Category> category)
{
state.addAll(category);
}
void add(Category category){
state = [
...state,
category,
];
}
}
file const.dart
final db_name = "quiz.db";
final columnMainCategoryId = "ID";
final columnCategoryName = "Name";
final columnCategoryImage = "Image";
final tableCategoryName = "Category";
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:quiz2/screens/home_page.dart';
void main() {
runApp(ProviderScope(child:MyApp()));
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
routes: {
"/homePage": (context) => MyCategoryPage(title: "My Quiz",)
},
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Topics'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.of(context).pop();
Navigator.pushNamed(context, "/homePage");
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
// This trailing comma makes auto-formatting nicer for build methods.
);
}
}
screens/home_page.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:quiz2/const/state.dart';
import 'package:quiz2/database/category_provider.dart';
import 'package:quiz2/database/db_helper.dart';
class MyCategoryPage extends StatefulWidget {
MyCategoryPage({Key key, this.title}):super(key: key);
final String title;
#override
_MyCategoryPageState createState() => _MyCategoryPageState();
}
class _MyCategoryPageState extends State<MyCategoryPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<List<Category>>(
future: getCategories(),
builder: (context, snapshot){
if(snapshot.hasError)
return Center(child: Text('${snapshot.error}'),);
else if(snapshot.hasData)
{
Category category = new Category();
category.ID = -1;
category.name = "Exam";
snapshot.data.add(category);
return GridView.count(
crossAxisCount: 2,
childAspectRatio: 1.0,
padding: const EdgeInsets.all(4.0),
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
children: snapshot.data.map((category){
return GestureDetector(child: Card(
elevation: 2,
color: category.ID == -1 ? Colors.green : Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: AutoSizeText(
"${category.name}",
style: TextStyle(
color: category.ID == -1 ? Colors.white : Colors.black,
fontWeight: FontWeight.bold
),
maxLines: 1,
textAlign: TextAlign.center,
)
,)
],
),
),);
}).toList(),
);
}
else return Center(child: CircularProgressIndicator(),);
},
)
);
}
Future<List<Category>> getCategories() async {
var db = await copyDB();
var result = await CategoryProvider().getCategories(db);
context.read(categoryListProvider).state = result;
return result;
}
}
db_helper.dart
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:quiz2/const/const.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
Future<Database> copyDB() async{
var dbPath = await getDatabasesPath();
var path = join(dbPath, db_name);
var exists = await databaseExists(path);
if(!exists){
try{
await Directory(dirname(path)).create(recursive:true);
} catch(_){
}
ByteData data = await rootBundle.load(join("assets/db",db_name));
List<int> bytes = data.buffer.asUint8List(data.offsetInBytes,data.lengthInBytes);
await File(path).writeAsBytes(bytes, flush:true);
}else{
print("DB already exists!");
}
return await openDatabase(path, readOnly: true);
}
state.dart
import 'package:flutter_riverpod/all.dart';
import 'package:quiz2/database/category_provider.dart';
final categoryListProvider = StateNotifierProvider((ref) => new CategoryList([]));
NosuchmethodError: Class 'List' has no instance setter 'state='.
Recevier: Instance(length:0) of ' _GrowableList'
Tried calling: state=Instance(lenght:15) of'_GrowableList'
In your code, you have
Future<List<Category>> getCategories() async {
var db = await copyDB();
var result = await CategoryProvider().getCategories(db);
context.read(categoryListProvider).state = result;
return result;
}
Here, your context.read(categoryListProvider) is actually of type List.
That's why, when you are calling it like that, it means that you are calling,
[/* some kind of list */].state = result
But, the List class doesn't have a property called state but you are trying to modify it. That's why the error is coming.
Comment out that line and check.