Flutter : SQFLITE Insert Image from API - flutter

I'm working with Flutter SQFlite.
I have App News and in this App have feature read News Offline, So i go to with SQFlite.
I have been to Insert and Fetch data from SQFlite , But the problem is Image what I insert from API , so if i don't have connection i can't fetch the Image.
I'm Thinking about firstly download Image from API , then save into directory device and calling Image from directory based on ID News.
Can you help me with this case ?
Thank's
My Full Source Code
import 'package:flutter_news/model/Bookmark_model.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:io';
class DBProvider {
DBProvider._();
static final DBProvider db = DBProvider._();
static Database _database;
Future<Database> get database async {
if (_database != null) return _database;
_database = await initDB();
return _database;
}
initDB() async {
Directory documentDirectory = await getApplicationDocumentsDirectory();
String path = join(documentDirectory.path, "flutter_news.db");
return await openDatabase(path,
version: 1, onCreate: _onCreate, onUpgrade: _onUpgrade);
}
void _onUpgrade(Database db, int oldVersion, int newVersion) {
//Run Migration according database versions
}
void _onCreate(Database db, int version) async {
var sqlCreateTable = '''CREATE TABLE bookmark(
id_berita TEXT,
judul_berita TEXT,
isi_berita TEXT,
gambar_berita TEXT,
nama_kategori TEXT,
nama_wartawan TEXT,
tanggal_berita TEXT
)''';
await db.execute(sqlCreateTable);
print("Table Bookmark Has Been Created");
}
// Future newBookmark(Bookmark newBookmark) async {
// final db = await database;
// var res = await db.rawInsert(
// "INSERT INTO tb_bookmark (id_berita,judul_berita,isi_berita,nama_kategori,nama_wartawan,tanggal_berita)VALUES(\'${newBookmark.idBerita}\',\'${newBookmark.judulBerita}\',\'${newBookmark.isiBerita}\',\'${newBookmark.namaKategori}\',\'${newBookmark.namaWartawan}\',\'${newBookmark.tanggalBerita}\')");
// print("Print : $res");
// return res;
// }
Future addBookmark(Bookmark newBookmark) async {
print(newBookmark);
var db = await database;
int res = await db.insert("bookmark", newBookmark.toJson());
print('Bookmark has been Added $res');
return res;
}
Future<List<Bookmark>> getAllBookmark() async {
final db = await database;
var res = await db.query('bookmark');
List<Bookmark> list = res.isNotEmpty
? res.map((json) => Bookmark.fromJson(json)).toList()
: [];
return list;
}
}
My Code Fetch Data
return Container(
child: FutureBuilder(
future: DBProvider.db.getAllBookmark(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
case ConnectionState.active:
return LoadingIndicator();
break;
default:
if (!snapshot.hasData) {
return DataNotFound(
iconData: Icons.sentiment_dissatisfied,
sizeIcon: 100,
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return Text('${snapshot.data[index].gambarBerita}');
},
);
}
}
},
),
);

Related

How to initialize SQLite connection

I new in Flutter.
I have already exist db and need add it to app.
Put file in assets/db/sms.db
in file pubspec.yaml
assets:
- assets/db/sms.db
Copy db file in applicationDirectory.
In file categoriesList.dart I try connect to it.
SmsDataBaseHelper.instance.getAllCategories()
I get error message.
database_helper.dart
import 'package:flutter/services.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
import 'dart:io' as io;
import '/models/categories.dart';
class SmsDataBaseHelper {
static final SmsDataBaseHelper instance = SmsDataBaseHelper._();
static Database? _db;
SmsDataBaseHelper._();
Future<Database> get db async {
_db ??= await _init();
return _db!;
}
Future<Database> _init() async {
io.Directory applicationDirectory = await getApplicationDocumentsDirectory();
String dbPathSms = path.join(applicationDirectory.path, "sms.db");
bool dbExistsSms = await io.File(dbPathSms).exists();
if (!dbExistsSms) {
// Copy from asset
ByteData data = await rootBundle.load(path.join("assets", "db", "sms.db"));
List<int> bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
// Write and flush the bytes written
await io.File(dbPathSms).writeAsBytes(bytes, flush: true);
}
return await openDatabase(dbPathSms);
}
/// get all the words from categories
Future<List<Categories>> getAllCategories() async {
if (_db == null) {
throw "_db is not initiated, initiate using [init(db)] function";
}
List<Map>? categories;
await _db!.transaction((txn) async {
categories = await txn.query(
"categories",
columns: [
"name",
"ru",
],
);
});
return categories!.map((e) => Categories.fromJson(e as Map<String, dynamic>)).toList();
}
}
categoriesList.dart
import 'package:flutter/material.dart';
import 'package:sqflite/sqflite.dart';
import '../utils/database_helper.dart';
import '../models/categories.dart';
class CategoriesList extends StatefulWidget {
const CategoriesList({super.key});
#override
State<CategoriesList> createState() => _CategoriesListState();
}
class _CategoriesListState extends State<CategoriesList> {
//late List<Categories> categoriesList;
#override
void initState(){
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('SMS Home Page'),
),
body: FutureBuilder<List<Categories>>(
future: SmsDataBaseHelper.instance.getAllCategories(),
builder: (BuildContext context, AsyncSnapshot<List<Categories>> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return const Text('start widget');
case ConnectionState.active:
case ConnectionState.waiting:
return const Text('Awaiting result from api...');
case ConnectionState.done:
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
return Text('Result: ${snapshot.data}');
}
},
),
);
}
}
use this class to build database ..
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class SqlDb {
static Database? _db;
Future<Database?> get db async {
if (_db == null) {
_db = await initialDb();
return _db;
} else {
return _db;
}
}
initialDb() async {
String databasepath = await getDatabasesPath();
String path = join(databasepath, 'shop.db');
Database mydb = await openDatabase(path,
onCreate: _onCreate, version: 1, onUpgrade: _onUpgrade);
return mydb;
}
_onUpgrade(Database db, int oldversion, int newversion) async {}
deletemydatabase() async {
String databasepath = await getDatabasesPath();
String path = join(databasepath, 'shop.db');
var x = await deleteDatabase(path);
return x;
}
_onCreate(Database db, int version) async {
await db.execute(
"CREATE TABLE cart (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT , title TEXT, size TEXT , code TEXT , img TEXT , price TEXT )");
}
//Select
readData(String sql) async {
Database? mydb = await db;
List<Map> response = await mydb!.rawQuery(sql);
return response;
}
//insert data
insertData(String sql) async {
Database? mydb = await db;
int response = await mydb!.rawInsert(sql);
return response;
}
deleteData(String sql) async {
Database? mydb = await db;
int response = await mydb!.rawDelete(sql);
return response;
}
updateData(String sql) async {
Database? mydb = await db;
int response = await mydb!.rawUpdate(sql);
return response;
}
}

Integrate Provider with SharedPreferences to save and get Provider data

The list which stores the task data is integrated with provider for state management, but once I close the app and reopen it again, all tasks vanish.
With resources, I got to know about SharedPreferences.
How do I go about saving and getting the data using shared preferences. I have given the code a try, but does not seem to work in my favor.
void saveData() async {
final prefs = await SharedPreferences.getInstance();
final String encodedData = Task.encode(tasks);
await prefs.setString('task_data', encodedData);
}
void getData() async {
final prefs = await SharedPreferences.getInstance();
final String taskString = prefs.getString('task_data').toString();
List<Task> tasksData = Task.decode(taskString);
_tasks = tasksData;
}
encode() and decode() functions help in mapping List to String and String to List respectively.
static String encode(List<Task> tasks) {
return jsonEncode(
tasks.map<Map<String, dynamic>>((task) => Task.toMap(task)).toList(),
);
}
static List<Task> decode(String tasks) {
var data = (jsonDecode(tasks) as List<dynamic>?);
if (data != null) {
return (jsonDecode(tasks) as List<dynamic>?)!.map<Task>((task) {
return Task.fromJson(task);
}).toList();
} else {
return <Task>[];
}
}
The Task list in displayed using ListView.
Widget build(BuildContext context) {
return Consumer<TaskData>(
builder: (context, taskData, child) {
taskData.getData();
return ListView.builder(
itemCount: taskData.taskCount,
itemBuilder: (context, index) {
taskData.sortTaskList();
final task = taskData.tasks[index];
return TaskTile(
taskTitle: task.name,
isChecked: task.isDone,
checkboxCallBack: (checkBoxState) async {
taskData.upDateTask(task);
taskData.saveData();
},
longPressCallBack: () async {
taskData.removeTask(task);
taskData.saveData();
},
);
},
);
},
);
}
I am expecting that you're using ChangeNotifier with Provider package in TaskData class.
In this case you have to add notifyListener() inside getData() because it is async task and you are updating values.
Future<void> getData() async {
final prefs = await SharedPreferences.getInstance();
final String taskString = prefs.getString('task_data').toString();
List<Task> tasksData = Task.decode(taskString);
_tasks = tasksData;
notifyListener(); // Add this line
}

Flutter provider listeners not updating themselves when sqflite database data changes

I created a local database using flutter sqflite. And I want to listen to a length of a list of tasks on that database and update the total count of the tasks, when I add something or remove from that list. But when I call provider.of(context) thing, it doesn't update themselves, means it doesn't listen. I used a stream to grab the database data and show in the UI.
Here is the database class I created:
class TaskDatabase with ChangeNotifier {
final String dbName = 'db.sqlite';
Database? _db;
List<Task> _tasksList = [];
int _totalTaskCount = 0;
final _streamController = StreamController<List<Task>>.broadcast();
Stream<List<Task>> all() =>
_streamController.stream.map((tasks) => tasks..sort());
int get totalTasksCount {
return _totalTaskCount;
}
Future<bool> close() async {
final db = _db;
if (db == null) {
return false;
}
await db.close();
return true;
}
Future<bool> open() async {
if (_db != null) {
return true;
}
final directory = await getApplicationDocumentsDirectory();
final path = '${directory.path}/$dbName';
try {
final db = await openDatabase(path);
_db = db;
//creating the database table using sqflite
const createTable = '''CREATE TABLE IF NOT EXISTS "TABLEOFTASKS" (
"id" INTEGER NOT NULL,
"taskTitle" TEXT,
"isDone" INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY("id" AUTOINCREMENT));''';
await db.execute(createTable);
// read all existing task objects from the db
_tasksList = await _fetchTasks();
_streamController.add(_tasksList);
return true;
} catch (e) {
// print('error = $e');
return false;
}
}
// Creating a new task and save to the database:
// other CRUD functions are not added here:)
Future<bool> create(String taskTitle) async {
final db = _db;
if (db == null) {
return false;
}
try {
final id = await db.insert(
'TABLEOFTASKS',
{
'taskTitle': taskTitle,
'isDone': 0,
},
);
final task = Task(
id: id,
taskTitle: taskTitle,
isDone: false,
);
_tasksList.add(task);
_streamController.add(_tasksList);
_totalTaskCount = _tasksList.length;
notifyListeners();
return true;
} catch (e) {
print('error in creating task = $e');
return false;
}
}
}
Here is the widget that I want to listen and update:
final int taskCount = Provider.of<TaskDatabase>(context, listen: true).totalTasksCount;
.
.
.
Text(taskCount.toString()),
I added the provider at the top of the widget tree and there are no errors. Only thing happening is not updating the text widget
I created a streamBuilder and grabbed the list I want as a snapshot. Updating the list length using the provider package did not work. You can find in the DB class in the question to find how I created a stream of Tasks. Firest initialize the Database in init method.
late final TaskDatabase _crudStorage;
#override
void initState() {
_crudStorage = TaskDatabase();
_crudStorage.open();
super.initState();
}
#override
void dispose() {
_crudStorage.close();
super.dispose();
}
....
return Scaffold(
resizeToAvoidBottomInset: false,
drawer: const CustomDrawer(),
body: StreamBuilder(
stream: _crudStorage.all(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.active:
case ConnectionState.waiting:
if (snapshot.data == null) {
return const Center(child: Shimmer());
}
final tasksList = snapshot.data as List<Task>; /// The List I want
.
.
.
.
.
.
SomeTextWidget('The length of tasks = ${tasksList.length}'),

Unable to display ListView from SqFLite

My data is able to upload to the database without any error, however i cant seem to display my listview with the data.
As you can see from the _submit() function, if theres an error, snackbar will be shown indicating theres an error and will not proceed to the mainpage, however, the result shows a snackbar with a success message,
So Im suspecting it has to do with my listview code, or my databasehelper as I may have missed out something in the code.
Any help is deeply appreciated!
Heres my listview code:
FutureBuilder<List<Note>>(
future: _databaseHelper.getNoteList(),
builder: (BuildContext context, AsyncSnapshot<List<Note>> snapshot){
if(snapshot.hasData){
return ListView.builder(
itemCount: _count,
itemBuilder: (BuildContext context, int position) {
Note note = snapshot.data[position];
return Card(
color: Colors.white,
elevation: 2.0,
child: new ListTile(
title: new Text(note.title),
subtitle: new Text(note.bodyText),
onTap: () =>
_navigateToEditAddPage(note, 'Edit a Note'),
onLongPress: () => _showDeleteDialog(note),
),
);
});
}else{
return Container(width: 0,height: 0,);
}
},),
Heres my insertData code:
void _submit() async {
if (_formKey.currentState.validate()) {
note.title = _titleController.text;
note.bodyText = _bodyTextController.text;
note.date = _dateController.text;
if (note.id == null) {
int result = await _databaseHelper.insertData(note);
if (result != 0) {
_moveToHomePage('Note successfully added');
} else {
_showSnackBar('Note unable to be inserted due to some error');
}
} else {
int result = await _databaseHelper.updateData(note);
if (result != 0) {
_moveToHomePage('Note successfully updated');
} else {
_showSnackBar('Note unable to be updated due to some error');
}
}
}
}
Heres my DatabaseHelper code:
class DatabaseHelper {
static Database _database;
String dataTable = 'NoteTable';
String colId = 'id';
String colTitle = 'title';
String colBody = 'bodyText';
String colDate = 'date';
DatabaseHelper._();
static final DatabaseHelper db = DatabaseHelper._();
Future<Database> get database async {
if (_database == null) {
_database = await initializeDatabase();
}
return _database;
}
Future<Database> initializeDatabase() async {
Directory directory = await getApplicationDocumentsDirectory();
String path = directory.path + 'notes.db';
var notesDatabase =
await openDatabase(path, version: 1, onCreate: _createDb);
return notesDatabase;
}
void _createDb(Database database, int newVersion) async {
await database.execute("CREATE TABLE $dataTable ("
"$colId INTEGER PRIMARY KEY AUTOINCREMENT,"
"$colTitle TEXT,"
"$colBody TEXT,"
"$colDate TEXT"
")");
}
Future<List<Map<String, dynamic>>> getNoteListMap() async {
Database db = await this.database;
var result = await db.query(dataTable);
return result;
}
Future<int> insertData(Note note) async {
Database db = await this.database;
var result = await db.insert(dataTable,note.toMap(),conflictAlgorithm:
ConflictAlgorithm.replace,);
return result;
}
Future<int> updateData(Note note) async {
Database db = await this.database;
var result = await db.update(dataTable,note.toMap(),
where: 'colId = ?', whereArgs: [note.id]);
return result;
}
Future<int> deleteData(Note note) async {
Database db = await this.database;
var result = await db
.delete(dataTable, where: 'colId = ?', whereArgs: [note.id]);
return result;
}
Future<int> getCount() async{
Database db = await this.database;
List<Map<String,dynamic>> x = await db.rawQuery('SELECT COUNT (*) from $dataTable');
int result = Sqflite.firstIntValue(x);
return result;
}
Future<List<Note>> getNoteList() async {
var noteMapList = await getNoteListMap();
int count = noteMapList.length;
//list of notes, each note consist of their own independent variables
List<Note> noteList;
for (int i = 0; i < count; i++) {
noteList.add(Note.fromMapObject(noteMapList[i]));
}
return noteList;
}
}
And lastly my Note model:
class Note {
int _id;
String _date;
String _title;
String _bodyText;
Note(this._date, this._title, this._bodyText);
Note.withId(this._id, this._date, this._title, this._bodyText);
set date(String date) {
this._date = date;
}
get date => _date;
set title(String title) {
this._title = title;
}
get title => _title;
set bodyText(String bodyText){
this._bodyText = bodyText;
}
get bodyText => _bodyText;
get id => _id;
Map<String, dynamic> toMap() {
var map = new Map<String, dynamic>();
if (_id != null) {
map['id'] = _id;
}
map['title'] = _title;
map['bodyText'] = _bodyText;
map['date'] = _date;
return map;
}
//Converting a map object to a note object
Note.fromMapObject(Map<String,dynamic> fromMap){
_id = fromMap['id'];
_title = fromMap['title'];
_bodyText = fromMap['bodyText'];
_date = fromMap['date'];
}
}
I found two errors in your code.
1: in getNoteList() of DatabaseHelper
List<Note> noteList;
to
List<Note> noteList = [];
2: in listview code
itemCount: _count,
to
itemCount: snapshot.data.length,
result:

Execute if database exists flutter

So basically what i want to do is:
When the user turns on the app for the first time,
The SQLite database is created and the data is fetched from the internet.
Until this is done, SetupPage() widget is displayed in scaffolds body or else Home() is displayed.
Now the code i wrote works perfectly for the first time, but when i open it the second time,
The SetupPage() only shows, it never goes back to the Home(). What am i doing wrong here ?
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart';
import 'package:path/path.dart';
import 'pages/home.dart';
import 'pages/SetUpPage.dart';
import 'package:sqflite/sqflite.dart';
class App extends StatefulWidget {
createState() {
return AppState();
}
}
class AppState extends State<App> {
final bgColor = const Color(0xFF1abc9c);
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
bool status = false;
Database database;
#override
void initState() {
initializeData();
super.initState();
}
void initializeData() async
{
var databasesPath = await getDatabasesPath();
String path = join(databasesPath, 'demo.db');
status = false;
Database database = await openDatabase(path, version: 1,
onCreate: (Database db, int version) async {
await db.execute(
'CREATE TABLE news (id INTEGER PRIMARY KEY, topic TEXT, img TEXT, newstitle TEXT, news TEXT, newslink TEXT)');
fetchData();
}
);
database.close();
}
#override
Widget build(context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text("UnFound News"),
backgroundColor: bgColor,
),
body: status ? Home() : SetUpPage(),
);
}
/*Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);*/
void fetchData() async {
var result = await get("https://api.myjson.com/bins/a0bvu");
var arr = json.decode(result.body)['post'];
for(int i = 0; i < arr.length; i++)
{
//TODO: Implement addition to database.
}
setState(() {
status = true;
});
}
}
You probably want to put the fetchData in a onOpen parameter instead of the onCreate parameter like this:
Database database = await openDatabase(path, version: 1,
onCreate: (Database db, int version) async {
await db.execute(
'CREATE TABLE news (id INTEGER PRIMARY KEY, topic TEXT, img TEXT, newstitle TEXT, news TEXT, newslink TEXT)');
await fetchData();
}, onOpen: (Database db) async {
setState(() {
status = true;
});
});
static Database _db;
Future<Database> get db async {
if(_db != null)
return _db;
_db = await initDb();
return _db;
}
//Creating a database with name test.dn in your directory
initDb() async {
io.Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, "test.db");
var theDb = await openDatabase(path, version: 1, onCreate: _onCreate);
return theDb;
}
checkDb(){
//do operation
var dbClient = await db;
}