Execute if database exists flutter - 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;
}

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;
}
}

Singleton doesn't have unique instance Flutter

I'm trying to implement the singleton pattern with null safety in Flutter, to do a unique instance for my sqflite database, but after initialization in the splashScreen, when I try to access it in another Widget, it seems that it's not the same instance since the database is not initialized. I tried to add a random() int to check if it has the same value in the different widget, and the value change each times I request it.
My code is like this :
class DatabaseHandler {
final int random = new Random().nextInt(200);
static final DatabaseHandler instance = new DatabaseHandler._internal();
factory DatabaseHandler() {
return instance;
}
DatabaseHandler._internal();
late Database _database;
Database getDb() {
print(random);
return _database;
}
Future<void> initDB() async {
var path = await getDatabasesPath();
var dbPath = join(path, 'test.db');
Database dbConnection = await openDatabase(dbPath, version: 1,
onCreate: (Database db, int version) async {
return db.execute(
"CREATE TABLE favorite_page(id TEXT PRIMARY KEY, isFavorite BOOL)",
);
});
this._database = dbConnection;
}
}
I got a "Not initialize exception" when I do getDb() even if I used initDB() in the splashScreen.
I call the singleton in two places :
The db is initialized here :
class _SplashPageState extends State<SplashPage> {
static const String route = "/splash";
void initializeFlutterFire() async {
// Wait for Firebase to initialize and set `_initialized` state to true
await Firebase.initializeApp();
PushNotificationService pushNotif = PushNotificationService();
pushNotif.initialise();
}
#override
initState() {
super.initState();
WidgetsFlutterBinding.ensureInitialized();
initializeFlutterFire();
DatabaseHandler().initDB();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Lottie.asset('assets/lotties/loading-screen.json',
animate: true, repeat: true)));
}
}
and used here :
class PagesDb {
Future<FavoritePage> insert(FavoritePage page) async {
var db = DatabaseHandler().getDb();
await db.insert(tablePage, page.toMap());
return page;
}
}
But I tried to just use DatabaseHandler().getDb() in different widgets, they got the same random value, but not the same as in the splash screen

Flutter's sqflite queries

I am working with an already existing database dictionary.db that has a table words and has 4 columns id, englishWord, germanWord, isFavorite.
Using Sqflite I am trying to return a list of German words based on an input in English pretty much the userflow works like this: user input an English word (example: "Ability") and I return a list of synonyms to that word in German.
My problem is I don't know how to do it the method I am trying returns the full list instead of only the intended search results
This is my Entity:
class Word {
final String id;
final String eng;
final String ger;
final String isFav;
Word({this.id, this.eng, this.ger, this.isFav});
Map<String, dynamic> toMap() {
return {
'wordId': id,
'englishWord': eng,
'germanWord': ger,
'isFavorite': isFav,
};
}
factory Word.fromMap(Map<String, dynamic> json) => new Word(
id: json['wordId'],
eng: json['englishWord'],
ger: json['germanWord'],
isFav: json['isFavorite']
);
}
And this is my database helper class:
class DatabaseHelper{
DatabaseHelper._();
static final DatabaseHelper databaseHelper = DatabaseHelper._();
Database _database;
static const String DB_NAME = "dict.db";
static const String TABLE = "words";
static const String ID = "wordId";
static const String ENGLISH_WORD = "englishWord";
static const String GERMAN_WORD = "germanWord";
static const String IS_FAV = "isFavorite";
Future<Database> get database async {
if (_database != null) return _database;
_database = await getDatabaseInstance();
return _database;
}
Future<Database> getDatabaseInstance() async {
io.Directory directory = await getApplicationDocumentsDirectory();
String path = join(directory.path, DB_NAME);
return await openDatabase(path, version: 1,
onCreate: (Database db, int version) async {
await db.execute("CREATE TABLE IF NOT EXISTS $TABLE ("
"$ID TEXT,"
"$ENGLISH_WORD TEXT,"
"$GERMAN_WORD TEXT,"
"$IS_FAV TEXT"
")");
});
}
//This is where I get the search result list
//I am stuck here I don't know where to go from here, I only get the full list
Future<List<Word>> searchEnglishResults(String userSearch) async{
final db = await database;
var response = await db.query("Word");
List<Word> list = response.map((c) => Word.fromMap(c)).toList();
return list;
}
I assumed the database is like below:
|id | englidhWord | germanWord | isFavorite |
|-------|-------------|-------------------|------------|
| 'id0' | 'bye' | 'Tschüss' | 'false' |
| 'id1' | 'bye' | 'Auf Wiedersehen' | 'true' |
so if the user searches for "bye", she/he should receive [ 'Tschüss', 'Auf Wiedersehen'](the Word model not only the German words).
For that, query has some options like where and whereArgs that you can use to search for a specific row on the database.
Here we want to search the database for rows that their englidhWord field has a value of bye.
Here is the edited version of your DB:
import 'dart:io';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sudoku/src/word.dart';
class DatabaseHelper {
DatabaseHelper._();
static final DatabaseHelper databaseHelper = DatabaseHelper._();
Database _database;
String DB_NAME = "dict.db";
static const String TABLE = "words";
static const String ID = "wordId";
static const String ENGLISH_WORD = "englishWord";
static const String GERMAN_WORD = "germanWord";
static const String IS_FAV = "isFavorite";
Future<Database> get database async {
if (_database != null) return _database;
_database = await getDatabaseInstance();
return _database;
}
Future<Database> getDatabaseInstance() async {
Directory directory = await getApplicationDocumentsDirectory();
String path = join(directory.path, DB_NAME);
return await openDatabase(path, version: 1,
onCreate: (Database db, int version) async {
await db.execute("CREATE TABLE IF NOT EXISTS $TABLE ("
"$ID TEXT,"
"$ENGLISH_WORD TEXT,"
"$GERMAN_WORD TEXT,"
"$IS_FAV TEXT"
")");
});
}
//add new words
Future<int> add(Word word) async {
final db = await database;
var response = await db.insert(TABLE, word.toMap());
return response;
}
//This is where I get the search result list
//I am stuck here I don't know where to go from here, I only get the full list
Future<List<Word>> searchEnglishResults(String userSearch) async {
final db = await database;
var response = await db
.query(TABLE, where: '$ENGLISH_WORD = ?', whereArgs: [userSearch]);
List<Word> list = response.map((c) => Word.fromMap(c)).toList();
return list;
}
}
And I show the result with listview on a page named MyApp
import 'package:flutter/material.dart';
import 'package:sudoku/src/db.dart';
import 'package:sudoku/src/word.dart';
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
DatabaseHelper db;
#override
void initState() {
db = DatabaseHelper.databaseHelper;
add();
super.initState();
}
add() async {
// await db.add(Word(eng: 'hi', ger: 'Hallo', id: 'id2', isFav: 'true'));
// await db.add(Word(eng: 'bye', ger: 'Tschüss', id: 'id0', isFav: 'fasle'));
// await db.add(
// Word(eng: 'bye', ger: 'Auf Wiedersehen', id: 'id0', isFav: 'true'));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: FutureBuilder<List<Word>>(
future: db.searchEnglishResults('bye'),
builder: (BuildContext context, AsyncSnapshot<List<Word>> snapshot) {
if (snapshot.hasData)
return ListView.builder(
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(snapshot.data[index].ger),
subtitle: Text(snapshot.data[index].eng),
trailing: Text(snapshot.data[index].isFav),
);
},
itemCount: snapshot.data.length,
);
return Center(
child: CircularProgressIndicator(),
);
},
),
);
}
}
I included a function named "add" in database and MyApp's initState to add new words to database.
If you refresh app for multiple time, it will add repetitive rows, the reason is that you should make one of the DB field unique, here can be id to do so you should create the table like below:
return await openDatabase(path, version: 1,
onCreate: (Database db, int version) async {
await db.execute("CREATE TABLE IF NOT EXISTS $TABLE ("
"$ID TEXT NOT NULL PRIMARY KEY,"
"$ENGLISH_WORD TEXT,"
"$GERMAN_WORD TEXT,"
"$IS_FAV TEXT"
")");
});
Also in add function in DB, you should choose what should be done when it faces to a repetitive row(Word here), there are some options, one can be to replace it, it should change like this:
//add new words
Future<int> add(Word word) async {
final db = await database;
var response = await db.insert(TABLE, word.toMap(), conflictAlgorithm: ConflictAlgorithm.replace);
return response;
}
I hope it was what You were looking for.

my list is not displaying when reading async from database

I have a screen that is reading data from a database table (sqllite) but the data is not been display in my listview. it looks like when i read the data the screen already display the gui and my array is not populated with data by the time the listview access the array.
below is my code
import 'package:finsec/model/cardview_list_item.dart';
import 'package:flutter/material.dart';
import 'package:finsec/utils/strings.dart';
import 'package:finsec/utils/colors.dart';
import 'package:finsec/widget/circle_icon.dart';
import 'package:finsec/data/db_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:finsec/utils/queries.dart';
class CardviewListItemData extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return CardviewListItemDataState();
}
}
class CardviewListItemDataState extends State<CardviewListItemData> {
DBProvider db = new DBProvider();
List<String> amountList;
List<CardviewListItem> listItems = List<CardviewListItem>();
int count = 0;
#override
Widget build(BuildContext context) {
if (amountList == null) {
amountList = List<String>();
updateListView();
listItems = summaryList();
}
return ListView.builder(
itemCount: listItems.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return ListTile(
leading: Icon(listItems[index].icon, color: listItems[index].iconColor),
title: Align(
child: new Text(listItems[index].title,style: TextStyle(fontSize: 16)),
alignment: Alignment(-1.4, 0),
),
trailing: new Text(listItems[index].amount, style: TextStyle(fontSize: 16),),
//onTap: () => onTapped(context, listItems[index].title),
);
},
);
}
List<CardviewListItem> summaryList() {
List<CardviewListItem> summaryListItems = List<CardviewListItem>();
CardviewListItem income = new CardviewListItem(
title: totalIncome,
amount: amountList[0],
icon: Icons.attach_money,
iconColor: green
);
summaryListItems.add(income);
return summaryListItems;
}
void updateListView() {
final Future<Database> dbFuture = db.initializeDatabase();
dbFuture.then((database) {
Future<List<String>> incomeListFuture = db.getDataList(test);
incomeListFuture.then((amountList) {
setState(() {
this.amountList = amountList;
this.count = this.amountList.length;
});
});
});
}
}
my database code is
import 'dart:io';
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:finsec/model/transaction_content.dart';
import 'package:finsec/utils/tables.dart';
import 'package:finsec/model/income/income_dao.dart';
import 'package:finsec/model/income/income.dart';
import 'package:finsec/model/list_item.dart';
import 'package:finsec/model/dao.dart';
import 'package:finsec/model/cardview_list_item.dart';
import 'package:finsec/utils/strings.dart';
import 'package:finsec/utils/colors.dart';
import 'package:finsec/widget/circle_icon.dart';
import 'package:finsec/data/db_provider.dart';
class DBProvider {
static DBProvider db; // Singleton DBProvider
static Database _database; // Singleton Database
DBProvider._createInstance(); // Named constructor to create instance of DBProvider
factory DBProvider() {
if (db == null) {
db = DBProvider._createInstance(); // This is executed only once, singleton object
}
return db;
}
Future<Database> get database async {
if (_database == null) {
_database = await initializeDatabase();
}
return _database;
}
Future<Database> initializeDatabase() async {
// Get the directory path for both Android and iOS to store database.
Directory directory = await getApplicationDocumentsDirectory();
String path = directory.path + 'finsec.db';
// Open/create the database at a given path
var finsecDatabase = await openDatabase(path, version: 4, onCreate: _createDb, onUpgrade: _onUpgrade);
return finsecDatabase;
}
void _createDb(Database db, int newVersion) async {
// await db.execute('drop table income');
print('CREATE THE TABLE2');
await db.execute(incomeTable);
//await db.execute("CREATE TABLE User(id INTEGER PRIMARY KEY, firstname TEXT, lastname TEXT, dob TEXT)");
}
// UPGRADE DATABASE TABLES
void _onUpgrade(Database db, int oldVersion, int newVersion) async {
if (oldVersion < newVersion) {
await db.execute('drop table income');
await db.execute(incomeTable);
}
}
// Fetch Operation: Get all row objects from database
Future<List<Map<String, dynamic>>> getTransactions(String tableName) async {
Database db = await this.database;
//var result = await db.rawQuery(tableName);
var result = await db.query(tableName);
result.forEach((row) => print(row));
// var res = await db.insert('note', transactionType.toMap());
return result;
}
// Fetch Operation: Get all row objects from database
Future<List<Map<String, dynamic>>> getTransactionsQuery(String query) async {
Database db = await this.database;
var result = await db.rawQuery(query);
return result;
}
// Insert Operation: Insert a transaction object to database
Future<int> insertTransaction(String sqlQuery) async {
Database db = await this.database;
//var dao = new IncomeDao();
var result = await db.rawInsert(sqlQuery); //insert(noteTable, note.toMap());
//var result = await db.insert('noteTable', transaction);
return result;
}
// Update Operation: Update a Note object and save it to database
Future<int> updateTransaction(Map transaction) async {
var db = await this.database;
//var result = await db.rawUpdate(sql);// update(noteTable, note.toMap(), where: '$colId = ?', whereArgs: [note.id]);
var result = await db.update('noteTable', transaction);
return result;
}
// Delete Operation: Delete a transaction object from database
Future<int> deleteTransaction(int id) async {
var db = await this.database;
int result = await db.rawDelete('DELETE FROM noteTable WHERE ');
return result;
}
// Get number of Note objects in database
Future<int> getCount() async {
Database db = await this.database;
List<Map<String, dynamic>> x = await db.rawQuery('SELECT COUNT (*) from noteTable');
int result = Sqflite.firstIntValue(x);
return result;
}
// Get the 'Map List' [ List<Map> ] and convert it to 'Note List' [ List<Note> ]
Future<List<Income>> getIncomeList(String tableName) async {
var incomeMapList = await getTransactions(tableName); // Get 'Map List' from database
int count = incomeMapList.length; // Count the number of map entries in db table
List<Income> incomeList = List<Income>();
// For loop to create a 'Income List' from a 'Map List'
for (int i = 0; i < count; i++) {
incomeList.add(Income.fromMap(incomeMapList[i]));
}
return incomeList;
}
// Get the 'Map List' [ List<Map> ] and convert it to 'Note List' [ List<Note> ]
Future<List<String>> getDataList(String query) async {
var dataMapList = await getTransactionsQuery(query); // Get 'Map List' from database
int count = dataMapList.length; // Count the number of map entries in db table
List<String> dataList = List<String>();
Map<String, dynamic> queryData;
for (int i = 0; i < count; i++) {
queryData = dataMapList[i];
dataList.add(queryData["amount"]);
}
return dataList;
}
}
i think the problem is in the following portion of the code
if (amountList == null) {
amountList = List<String>();
updateListView();
listItems = summaryList();
}
updateListView() is getting called but it looks like the code continue executing to the next line even if updateListView didnt finish executing. when i called amountList[0] in summaryList(), i get the following error
RangeError (index): Invalid value: Valid value range is empty: 0
I/flutter ( 3640):
I/flutter ( 3640): User-created ancestor of the error-causing widget was:
I/flutter ( 3640): Column file:///C:/Users/rodrigue33/Documents/APP/finsec/lib/widget/cardview_widget.dart:45:22
I/flutter ( 3640):
I/flutter ( 3640): When the exception was thrown, this was the stack:
I/flutter ( 3640): #0 List.[] (dart:core-patch/growable_array.dart:145:60)
I/flutter ( 3640): #1 CardviewListItemDataState.summaryList (package:finsec/data/cardview_list_item.dart:114:27)
I/flutter ( 3640): #2 CardviewListItemDataState.build (package:finsec/data/cardview_list_item.dart:80:19)
I/flutter ( 3640): #3 StatefulElement.build (package:flutter/src/widgets/framework.dart:4040:27)
this is my assumption of what is happening. do anyone agree with me? how can i solve this issue of waiting for database to return data before displaying the data in the listview? thanks in advance
The problem is that you are calling the setState method and updating its "amountList", as setState reloads the page it doesn't quite execute "listItems = summaryList ();".
Perhaps a quick fix would be to create a bool check and leave it false, set to true when setState is executed, and within your builder method do:
if (amountList == null) {
amountList = List<String>();
updateListView();
}
if(check){
listItems = summaryList();
}
NOTE: I suggest using async and await in your methods instead of future.
void updateListView() async{
final Database db = await db.initializeDatabase();
List<String> incomeList = await db.getDataList(test);
setState(() {
this.amountList = amountList;
this.count = this.amountList.length;
this.check = true;
});
}
I hope I helped you!

Signing out of flutter app with sqflite database

I am trying to write the code for signing out of a flutter app logging in (The database of the map is made with sqflite).
However I am getting the following error message:
flutter: NoSuchMethodError: The method 'notify' was called on null.
Receiver: null
Tried calling: notify(Instance of 'AuthState')
Have provided code of required files below.
I am trying to incorporate a sign out function in the home_screen.dart file but I feel I am missing a link between the auth.dart, auth_provider.dart, login_screen.dart and home_screen.dart files. The codes of the required files for the issue are as follows:
File: database_helper.dart
import 'dart:async';
import 'dart:io' as io;
import 'package:path/path.dart';
import 'package:better_login/user.dart';
import 'package:sqflite/sqflite.dart' ;
import 'package:path_provider/path_provider.dart';
class DatabaseHelper {
static final DatabaseHelper _instance = new
DatabaseHelper.internal();
factory DatabaseHelper() => _instance;
static Database _db;
Future<Database> get db async {
if(_db != null)
return _db;
_db = await initDb();
return _db;
}
DatabaseHelper.internal();
initDb() async {
io.Directory documentsDirectory = await
getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, "main.db");
var theDb = await openDatabase(path, version: 1, onCreate:
_onCreate);
return theDb;
}
void _onCreate(Database db, int version) async {
// When creating the db, create the table
await db.execute(
"CREATE TABLE User(username TEXT,password TEXT)");
print("Created tables");
}
Future<int> saveUser(User user) async {
var dbClient = await db;
int res = await dbClient.insert("User", user.toMap());
return res;
}
Future<int> deleteUsers() async {
var dbClient = await db;
int res = await dbClient.delete("User");
return res;
}
Future<bool> isLoggedIn() async {
var dbClient = await db;
var res = await dbClient.query("User");
return res.length > 0? true: false;
}
}
File:auth.dart
import 'package:better_login/database_helper.dart';
enum AuthState{ LOGGED_IN, LOGGED_OUT }
abstract class AuthStateListener {
void onAuthStateChanged(AuthState state);
}
class AuthStateProvider {
static final AuthStateProvider _instance = new
AuthStateProvider.internal();
List<AuthStateListener> _subscribers;
factory AuthStateProvider() => _instance;
AuthStateProvider.internal() {
_subscribers = new List<AuthStateListener>();
initState();
}
void initState() async {
var db = new DatabaseHelper();
var isLoggedIn = await db.isLoggedIn();
if(isLoggedIn)
notify(AuthState.LOGGED_IN);
else
notify(AuthState.LOGGED_OUT);
}
void subscribe(AuthStateListener listener) {
_subscribers.add(listener);
}
void dispose(AuthStateListener listener) {
for(var l in _subscribers) {
if(l == listener)
_subscribers.remove(l);
}
}
void notify(AuthState state) {
_subscribers.forEach((AuthStateListener s) =>
s.onAuthStateChanged(state));
}
}
File: auth_provider.dart
import 'package:flutter/material.dart';
import 'package:better_login/auth.dart';
class AuthProvider extends InheritedWidget {
const AuthProvider({Key key, Widget child, this.auth}) : super(key: key, child: child);
final AuthStateListener auth;
#override
bool updateShouldNotify(InheritedWidget oldWidget) => true;
static AuthProvider of(BuildContext context) {
return context.inheritFromWidgetOfExactType(AuthProvider);
}
}
File: home_screen.dart
import 'package:flutter/material.dart';
import 'package:better_login/auth.dart';
import 'package:better_login/login_screen.dart';
import 'package:better_login/login_screen_presenter.dart';
import 'package:better_login/auth_provider.dart';
class HomeScreen extends StatelessWidget {
HomeScreen({this.authStateListener, this.authStateProvider});
final AuthStateListener authStateListener;
final AuthStateProvider authStateProvider;
void _signOut() async {
try{
authStateProvider.notify(AuthState.LOGGED_OUT);
authStateListener.onAuthStateChanged(AuthState.LOGGED_OUT);
}catch(e){
print(e);
}
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
title: new Text("Home"),
actions: <Widget>[
new IconButton(icon: new Icon(Icons.exit_to_app), onPressed: (){_signOut();}),
],
),
body: new Center(
child: new Text("Welcome home!"),
),
);
}
}