Related
I am creating a Database with SQLite in flutter I did three functions 1-Creating database 2- Insert into the database 3-Get data from the database, my app is about to enter the task and date of start and due date then print the tasks so when I run the app it must show int console 'database created, database opened, the table created and those sentences I wrote' but when I run the app this error shows up to me.
Debug service listening on ws://127.0.0.1:52431/lFIqFZgcPT4=/ws
Running with sound null safety
Connecting to VM Service at ws://127.0.0.1:52431/lFIqFZgcPT4=/ws
Error: MissingPluginException(No implementation found for method getDatabasesPath on channel com.tekartik.sqflite)
at Object.throw_ [as throw] (http://localhost:52322/dart_sdk.js:5067:11)
at MethodChannel._invokeMethod (http://localhost:52322/packages/flutter/src/services/restoration.dart.lib.js:1560:21)
at _invokeMethod.next (<anonymous>)
at http://localhost:52322/dart_sdk.js:40571:33
at _RootZone.runUnary (http://localhost:52322/dart_sdk.js:40441:59)
at _FutureListener.thenAwait.handleValue (http://localhost:52322/dart_sdk.js:35363:29)
at handleValueCallback (http://localhost:52322/dart_sdk.js:35931:49)
at Function._propagateToListeners (http://localhost:52322/dart_sdk.js:35969:17)
at _Future.new.[_completeWithValue] (http://localhost:52322/dart_sdk.js:35817:23)
at async._AsyncCallbackEntry.new.callback (http://localhost:52322/dart_sdk.js:35838:35)
at Object._microtaskLoop (http://localhost:52322/dart_sdk.js:40708:13)
at _startMicrotaskLoop (http://localhost:52322/dart_sdk.js:40714:13)
at http://localhost:52322/dart_sdk.js:36191:9
I tried to restart the app and flutter run, flutter clean and nothing worked
functions I wrote :
void createDatabase() async {
// ignore: unused_local_variable
database = await openDatabase(
'todo.db',
version: 1,
onCreate: (database, version) {
print('database created');
database
.execute(
'create table tasks(id INTEGER PRIMARY KEY , title TEXT , date TEXT , time TEXT , status TEXT)')
.then((value) {
print('table created');
}).catchError(
(error) {
print('error when creating table ${error.toString()}');
},
); //catcherror
},
onOpen: (database) {
getDataFromDatabase(database);
print('datatbase opened');
print('get from database done');
},
); //opendatabase
} //createdatabase
//Inserting to the database
Future insertToDatabase({
required String title,
required String time,
required String date,
}) async {
return await database!.transaction((txn) async {
txn
.rawInsert(
'INSERT INTO tasks(title,date,time,status) VALUES("$title","$time","$date","new")')
.then((value) {
print("$value Inserted succesfully");
}).catchError((error) {
print("Error when interting new record ${error.toString()}");
});
});
} //insert to database
void getDataFromDatabase(database) async {
// ignore: unused_local_variable
List<Map> tasks = await database!.rawQuery('SELECT * FROM tasks');
print(tasks);
}
}
code of the app :
// ignore_for_file: unnecessary_import
import 'package:app1/modules/archive%20tasks/Archived_tasks.dart';
import 'package:app1/modules/done%20tasks/Done_tasks.dart';
import 'package:app1/modules/newtasks/New_tasks.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:sqflite/sqflite.dart';
// ignore: camel_case_types
class Home_Layout extends StatefulWidget {
#override
Home_LayoutState createState() => Home_LayoutState();
}
// ignore: camel_case_types
class Home_LayoutState extends State<Home_Layout> {
//1-create database and create tables
//2-open database
//3- insert to database
//4-get from Database
//5-update in Database
//6-delete from database
// ignore: non_constant_identifier_names
int Currentindex = 0;
Database? database;
// ignore: non_constant_identifier_names
var Scaffoldkey = GlobalKey<ScaffoldState>();
// ignore: non_constant_identifier_names
var formKey = GlobalKey<FormState>();
bool isBottomSheetShown = false;
IconData fabicon = Icons.add;
List<Widget> screens = [
New_Tasks(),
Done_Tasks(),
Archived_Tasks(),
];
List<String> titles = [
'New Tasks',
'Done Tasks',
'Archived Tasks',
];
// ignore: non_constant_identifier_names
var TitleController = TextEditingController();
// ignore: non_constant_identifier_names
var TimeController = TextEditingController();
// ignore: non_constant_identifier_names
var DateController = TextEditingController();
#override
void initState() {
super.initState();
createDatabase();
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: Scaffoldkey,
appBar: AppBar(
title: Text(
titles[Currentindex],
),
),
body: screens[Currentindex],
floatingActionButton: FloatingActionButton(
onPressed: () {
if (isBottomSheetShown) {
if (formKey.currentState!.validate()) {
insertToDatabase(
title: TitleController.text,
time: TimeController.text,
date: DateController.text,
).then(
(value) {
Navigator.pop(context);
isBottomSheetShown = false;
setState(() {
fabicon = Icons.add;
});
},
);
}
} else {
Scaffoldkey.currentState!
.showBottomSheet(
(context) => Container(
color: Colors.grey[200],
padding: EdgeInsets.all(20.0),
child: Form(
key: formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
controller: TitleController,
keyboardType: TextInputType.text,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Task Title',
prefixIcon: Icon(
Icons.title,
),
),
validator: (value) {
if (value!.isEmpty) {
return ('Title must not be empty');
} else
return null;
},
),
SizedBox(
height: 15,
),
TextFormField(
controller: TimeController,
keyboardType: TextInputType.datetime,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Task Time',
prefixIcon: Icon(
Icons.watch_later_outlined,
),
),
onTap: () {
showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
).then(
(value) {
TimeController.text =
value!.format(context).toString();
print(value.format(context));
},
);
print('timing tapped');
},
validator: (value) {
if (value!.isEmpty) {
return ('Time must not be empty');
} else
return null;
},
),
SizedBox(
height: 15,
),
TextFormField(
controller: DateController,
keyboardType: TextInputType.datetime,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Task Date',
prefixIcon: Icon(
Icons.calendar_today,
),
),
onTap: () {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime.now(),
lastDate: DateTime.parse('2022-06-07'),
).then(
(value) {
print(DateFormat.yMMMd().format(value!));
DateController.text =
DateFormat.yMMMd().format(value);
},
);
},
validator: (value) {
if (value!.isEmpty) {
return ('date must not be empty');
} else
return null;
},
),
],
),
),
),
)
.closed
.then((value) {
isBottomSheetShown = false;
setState(() {
fabicon = Icons.add;
});
}); //bottomsheet
isBottomSheetShown = true;
setState(
() {
fabicon = Icons.edit;
},
);
}
},
child: Icon(
fabicon,
),
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.red,
elevation: 15.0,
currentIndex: Currentindex,
onTap: (index) {
setState(() {
Currentindex = index;
print(Currentindex);
});
},
items: [
BottomNavigationBarItem(
icon: Icon(
Icons.menu,
),
label: 'Tasks',
),
BottomNavigationBarItem(
icon: Icon(
Icons.check_circle_outline,
),
label: 'Done',
),
BottomNavigationBarItem(
icon: Icon(
Icons.archive_outlined,
),
label: 'Archived',
),
],
),
);
}
// Future<String> getname() async {
// return 'Ahmed Ali';
// }
void createDatabase() async {
// ignore: unused_local_variable
database = await openDatabase(
'todo.db',
version: 1,
onCreate: (database, version) {
print('database created');
database
.execute(
'create table tasks(id INTEGER PRIMARY KEY , title TEXT , date TEXT , time TEXT , status TEXT)')
.then((value) {
print('table created');
}).catchError(
(error) {
print('error when creating table ${error.toString()}');
},
); //catcherror
},
onOpen: (database) {
getDataFromDatabase(database);
print('datatbase opened');
print('get from database done');
},
); //opendatabase
} //createdatabase
//Inserting to the database
Future insertToDatabase({
required String title,
required String time,
required String date,
}) async {
return await database!.transaction((txn) async {
txn
.rawInsert(
'INSERT INTO tasks(title,date,time,status) VALUES("$title","$time","$date","new")')
.then((value) {
print("$value Inserted succesfully");
}).catchError((error) {
print("Error when interting new record ${error.toString()}");
});
});
} //insert to database
void getDataFromDatabase(database) async {
// ignore: unused_local_variable
List<Map> tasks = await database!.rawQuery('SELECT * FROM tasks');
print(tasks);
}
}
So i am a beginner in flutter and am trying to learn via tutorials, so here I am trying to make todo app using sqflite and everything is perfect and no error is shown in the editor but on clicking floating action button in notelist file it shows this error-
The following _TypeError was thrown building Builder:
type '_Type' is not a subtype of type 'String'
heres my main.dart file
void main() {
runApp(MaterialApp(
home: NoteList(),
));
}
here notelist
class NoteList extends StatefulWidget {
const NoteList({Key? key}) : super(key: key);
#override
_NoteListState createState() => _NoteListState();
}
class _NoteListState extends State<NoteList> {
int count = 0;
DatabaseHelper databaseHelper = DatabaseHelper();
late List<Note> noteList;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Note List'),
),
body: getNoteListView(),
floatingActionButton: FloatingActionButton(
onPressed: () {
debugPrint('fab clicked');
navigateToDetail(Note('', '', 2 ,''),'Add Note');
},
child: Icon(Icons.add),
),
);
}
ListView getNoteListView(){
return ListView.builder(
itemCount: count,
itemBuilder: (context, index){
return Card(
color: Colors.white,
elevation: 2.0,
child: ListTile(
leading: CircleAvatar(
backgroundColor: getPriorityColor(this.noteList[index].priority),
child: getPriorityIcon(this.noteList[index].priority),
),
title: Text(this.noteList[index].title!,),
subtitle: Text(this.noteList[index].date!),
trailing: IconButton(onPressed: (){
_delete(context, noteList[index]);
},
icon: Icon(Icons.delete),
),
onTap: (){
debugPrint('tapped');
navigateToDetail(noteList[index],'Edit Note');
},
),
);
}
);
}
void navigateToDetail(Note note, String title) async{
bool result = await Navigator.push(context, MaterialPageRoute(builder: (context) {
return NoteDetail(appBarTitle: Title, note: note);
}));
if (result == true) {
updateListView();
}
}
// Returns the priority color
Color getPriorityColor(int? priority) {
switch (priority) {
case 1:
return Colors.red;
break;
case 2:
return Colors.yellow;
break;
default:
return Colors.yellow;
}
}
// Returns the priority icon
Icon getPriorityIcon(int? priority) {
switch (priority) {
case 1:
return Icon(Icons.play_arrow);
break;
case 2:
return Icon(Icons.keyboard_arrow_right);
break;
default:
return Icon(Icons.keyboard_arrow_right);
}
}
void _delete(BuildContext context, Note note) async {
int? result = await databaseHelper.deleteNote(note.id);
if (result != 0) {
_showSnackBar(context, 'Note Deleted Successfully');
updateListView();
}
}
void _showSnackBar(BuildContext context, String message) {
final snackBar = SnackBar(content: Text(message));
Scaffold.of(context).showSnackBar(snackBar);
}
void updateListView() {
final Future<Database> dbFuture = databaseHelper.initializeDatabase();
dbFuture.then((database) {
Future<List<Note>> noteListFuture = databaseHelper.getNoteList();
noteListFuture.then((noteList) {
setState(() {
this.noteList = noteList;
this.count = noteList.length;
});
});
});
}
}
and heres notedetail file
class NoteDetail extends StatefulWidget {
final Note note;
final appBarTitle;
NoteDetail( {Key? key,required this.appBarTitle, required this.note}) : super(key: key);
#override
_NoteDetailState createState() => _NoteDetailState(this.note, this.appBarTitle);
}
class _NoteDetailState extends State<NoteDetail> {
static var _priorities = ['High', 'Low'];
DatabaseHelper helper = DatabaseHelper();
TextEditingController titleController = TextEditingController();
TextEditingController descController = TextEditingController();
String appBarTitle;
Note note;
_NoteDetailState(this.note , this.appBarTitle);
#override
Widget build(BuildContext context) {
titleController.text = note.title!;
descController.text = note.description!;
return Scaffold(
appBar: AppBar(
title: Text(appBarTitle),
),
body: Container(
padding: EdgeInsets.all(10),
child: ListView(
children: [
ListTile(
title: DropdownButton(
items: _priorities.map((dropDownStringItem) {
return DropdownMenuItem (
value: dropDownStringItem,
child: Text(dropDownStringItem),
);
}).toList(),
value: getPriorityAsString(note.priority),
onChanged: (valueSelectedByUser) {
setState(() {
debugPrint('User selected $valueSelectedByUser');
updatePriorityAsInt(valueSelectedByUser);
});
}
),
),
SizedBox(height: 10,),
Container(
child: TextField(
controller: titleController,
onChanged: (value) {
debugPrint('something changed in the title textfield ');
updateTitle();
},
decoration: InputDecoration(
labelText: 'Title',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
)
),
),
),
SizedBox(height: 10,),
Container(
child: TextField(
controller: descController,
onChanged: (value) {
debugPrint('something changed in the description textfield ');
updateDescription();
},
decoration: InputDecoration(
labelText: 'Description',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
)
),
),
),
Container(
padding: EdgeInsets.all(10),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 120,
height: 50,
padding: EdgeInsets.all(5),
child: ElevatedButton(onPressed: (){
debugPrint('add button clicked');
_save();
}, child: Text('Save',
style: TextStyle(
fontSize: 18
),
)
),
),
Container(
width: 120,
height: 50,
padding: EdgeInsets.all(5),
child: ElevatedButton(onPressed: (){
_delete();
debugPrint('Delete button clicked');
}, child: Text('Delete',
style: TextStyle(
fontSize: 18
),)),
),
],
),
)
],
),
),
);
}
// Convert int priority to String priority and display it to user in DropDown
String getPriorityAsString(int? value) {
String priority = '';
switch (value) {
case 1:
priority = _priorities[0]; // 'High'
break;
case 2:
priority = _priorities[1]; // 'Low'
break;
}
return priority;
}
// Convert the String priority in the form of integer before saving it to Database
void updatePriorityAsInt(var value) {
switch (value) {
case 'High':
note.priority = 1;
break;
case 'Low':
note.priority = 2;
break;
}
}
// Update the title of Note object
void updateTitle(){
note.title = titleController.text;
}
// Update the description of Note object
void updateDescription() {
note.description = descController.text;
}
void _delete() async {
moveToLastScreen();
// Case 1: If user is trying to delete the NEW NOTE i.e. he has come to
// the detail page by pressing the FAB of NoteList page.
if (note.id == null) {
_showAlertDialog('Status', 'No Note was deleted');
return;
}
// Case 2: User is trying to delete the old note that already has a valid ID.
int? result = await helper.deleteNote(note.id);
if (result != 0) {
_showAlertDialog('Status', 'Note Deleted Successfully');
} else {
_showAlertDialog('Status', 'Error Occured while Deleting Note');
}
}
void moveToLastScreen() {
Navigator.pop(context, true);
}
void _showAlertDialog(String title, String message) {
AlertDialog alertDialog = AlertDialog(
title: Text(title),
content: Text(message),
);
showDialog(
context: context,
builder: (_) => alertDialog
);
}
// Save data to database
void _save() async {
moveToLastScreen();
note.date = DateFormat.yMMMd().format(DateTime.now());
int? result;
if (note.id != null) { // Case 1: Update operation
result = await helper.updateNote(note);
} else { // Case 2: Insert Operation
result = await helper.insertNote(note);
}
if (result != 0) { // Success
_showAlertDialog('Status', 'Note Saved Successfully');
} else { // Failure
_showAlertDialog('Status', 'Problem Saving Note');
}
}
}
This looks like a spelling mistake.
void navigateToDetail(Note note, String title) async{
...
// change Title into title
return NoteDetail(appBarTitle: title, note: note);
...
I don't want to show Snackbar on stacked screen. When user taps on Signup from LoginScreen. Then, SignUpScreen stacked over LoginScreen. but the problem is both implements same ProviderListener and therefore, It shows Snackbar multiple times. How can I avoid it? , How can I make sure that, If current route is SignUp then show Snackbar
UserAuthService.dart
import 'package:notifications/domain/repository/firebase_repository/firebase_user_repo.dart';
import 'package:notifications/domain/services/auth_service/all_auth_builder.dart';
import 'package:notifications/export.dart';
import 'package:notifications/resources/local/local_storage.dart';
enum AuthenticationStatus {
loading,
error,
success,
}
class UserAuthService extends ChangeNotifier {
final authBuilder = AllTypeAuthBuilder();
String? _errorMsg, _sessionID;
AuthenticationStatus _status = AuthenticationStatus.loading;
EmailLinkAuthenticationRepo? _repo;
String? get errorMsg => this._errorMsg;
void get _setDefault {
_errorMsg = null;
//_sessionID = null;
}
String? get sessionID {
return LocallyStoredData.getSessionID();
}
void logOut() {
return LocallyStoredData.deleteUserKey();
}
Future<bool> userExists(String userID) async {
final isExists = await authBuilder.checkUserExists(userID);
return isExists ? true : false;
}
Future<bool> login() async {
_setDefault;
try {
await authBuilder.login();
return true;
} on BaseException catch (e) {
log("Exception $e");
_errorMsg = e.msg;
notifyListeners();
return false;
}
}
Future<bool> register(String username, String email, String password) async {
_setDefault;
try {
await authBuilder.register(username, email, password);
return true;
} on BaseException catch (e) {
log("Exception $e");
_errorMsg = e.msg;
notifyListeners();
return false;
}
}
Future<bool> signIn(String userID, String password) async {
_setDefault;
try {
await authBuilder.signIn(userID, password);
return true;
} on BaseException catch (e) {
log("Exception ${e.msg}");
_errorMsg = e.msg;
notifyListeners();
}
return false;
}
void loginWithEmail(String email) async {
_setDefault;
try {
_repo = await authBuilder.loginWithEmail(email);
_repo!.onLinkListener(
onSuccess: _onSuccess,
onError: _onError,
);
} on BaseException catch (e) {
log("Exception ${e.msg}");
_errorMsg = e.msg;
}
notifyListeners();
}
Future<bool> _onSuccess(PendingDynamicLinkData? linkData) async {
_setDefault;
try {
log("OnLinkAuthenticate");
await _repo!.onLinkAuthenticate(linkData);
return true;
} on BaseException catch (e) {
log("Error onSucess: $e");
_errorMsg = e.msg;
notifyListeners();
}
return false;
}
Future<dynamic> _onError(OnLinkErrorException? error) async {
log("Error $error in Link");
}
Future<void> tryTo(Function callback) async {
try {
await callback();
} on BaseException catch (e) {
_errorMsg = e.msg;
}
}
}
LoginScreen.dart
class LoginScreen extends StatefulWidget {
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _userIDController = TextEditingController(),
_passwordController = TextEditingController();
final _formKey = GlobalKey<FormState>();
bool? isAuthenticated;
#override
initState() {
super.initState();
}
#override
void dispose() {
_userIDController.dispose();
_passwordController.dispose();
super.dispose();
}
_onGoogleLogin() async {
context.read(loginPod).login();
}
_onLoginButtonTap() {
networkCheckCallback(context, () async {
if (_formKey.currentState!.validate()) {
WidgetUtils.showLoaderIndicator(context, 'Loading...');
final isSignedIn = await context
.read(loginPod)
.signIn(_userIDController.text, _passwordController.text);
Navigator.pop(context);
if (isSignedIn) Beamer.of(context).beamToNamed(Routes.home);
}
});
}
_resetAuthenticateState() {
if (isAuthenticated != null)
setState(() {
isAuthenticated = null;
});
}
onUsernameChange(String? value) async {
final error = await hasNetworkError();
if (_userIDController.text.isNotEmpty && error == null) {
isAuthenticated = await context.read(loginPod).userExists(value!);
setState(() {});
return;
}
_resetAuthenticateState();
}
onPasswordChange(String? value) {
//Code goes here....
}
loginWith(BuildContext context, LoginType type) {
switch (type) {
case LoginType.emailLink:
Beamer.of(context).beamToNamed(Routes.email_link_auth);
break;
case LoginType.idPassword:
Beamer.of(context).beamToNamed(Routes.login_id_pass);
break;
case LoginType.googleAuth:
Beamer.of(context).beamToNamed(Routes.login_with_google);
break;
case LoginType.unknown:
Beamer.of(context).beamToNamed(Routes.register);
break;
}
}
Future<bool> _onBackPress(_) async {
return await showDialog<bool>(
context: _,
builder: (context) {
return AlertDialog(
title: Text("Do you want to exit?"),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text("OK")),
TextButton(
onPressed: () {
Beamer.of(_).popRoute();
},
child: Text("Cancel"))
],
);
}) ??
false;
}
_onLoginStatus(BuildContext _, UserAuthService service) {
if (service.errorMsg != null)
_.showErrorBar(
content: Text(
"WithLogin" + service.errorMsg!,
style: TextStyle(fontSize: 12.sp),
));
}
#override
Widget build(BuildContext _) {
return ProviderListener(
onChange: _onLoginStatus,
provider: loginPod,
child: WillPopScope(
onWillPop: () => _onBackPress(_),
child: Scaffold(
body: SingleChildScrollView(
child: SizedBox(height: 1.sh, child: _buildLoginScreen())),
),
),
);
}
Widget _buildLoginScreen() {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
//_buildVrtSpacer(60),
_buildHeading(),
//_buildVrtSpacer(30),
_buildForm(),
//_buildVrtSpacer(30),
_buildIconButtons(),
_buildSignUpButton(),
],
);
}
BoldHeadingWidget _buildHeading() =>
BoldHeadingWidget(heading: AppStrings.login);
ResponsiveVrtSpacer _buildVrtSpacer(double value) =>
ResponsiveVrtSpacer(space: value);
Widget _buildForm() {
return CustomForm(
formKey: _formKey,
child: Column(
children: [
_buildUsernameField(),
_buildVrtSpacer(10),
_buildPasswordField(),
_buildForgetPassword(),
_buildLoginButton(),
],
),
);
}
Widget _buildPasswordField() {
return CustomTextFieldWithLabeled(
controller: _passwordController,
label: AppStrings.password,
hintText: AppStrings.password,
onValidate: (String? value) =>
(value!.isEmpty) ? AppStrings.emptyPasswordMsg : null,
obscureText: true,
onChange: onPasswordChange,
icon: CupertinoIcons.lock);
}
Widget _buildUsernameField() {
return CustomTextFieldWithLabeled(
controller: _userIDController,
label: AppStrings.usernameOrEmail,
hintText: AppStrings.usernameOrEmail1,
icon: CupertinoIcons.person,
onChange: onUsernameChange,
onValidate: (String? value) =>
(value!.isEmpty) ? AppStrings.emptyUserIDMsg : null,
suffixIcon: isAuthenticated == null
? null
: (isAuthenticated!
? CupertinoIcons.checkmark_alt_circle_fill
: CupertinoIcons.clear_circled_solid),
suffixColor: isAuthenticated == null
? null
: (isAuthenticated! ? Colors.green : Styles.defaultColor));
}
Widget _buildIconButtons() {
return Column(
children: [
Text("Or", style: TextStyle(fontSize: 14.sp)),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(width: 10),
_buildIconButton(
iconPath: 'assets/icons/email-icon.svg', onTap: () {}),
const SizedBox(width: 8),
_buildIconButton(
iconPath: 'assets/icons/icons8-google.svg',
onTap: _onGoogleLogin),
],
),
],
);
}
Widget _buildIconButton(
{required String iconPath, required VoidCallback onTap}) {
return GestureDetector(
onTap: onTap,
child: SvgPicture.asset(
iconPath,
height: 30.sp,
width: 30.sp,
));
}
Widget _buildSignUpButton() {
return Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
AppStrings.dontHaveAccount,
style: TextStyle(color: Colors.black54, fontSize: 14.sp),
),
const SizedBox(height: 5),
CustomTextButton(
title: "Sign Up",
onPressed: () => Beamer.of(context).beamToNamed(Routes.register)),
],
);
}
Widget _buildLoginButton() {
return DefaultElevatedButton(
onPressed: _onLoginButtonTap,
title: AppStrings.login,
);
}
Widget _buildForgetPassword() {
return Align(
alignment: Alignment.centerRight,
child: TextButton(
style: ButtonStyle(
overlayColor: MaterialStateProperty.all(Color(0x11000000)),
foregroundColor: MaterialStateProperty.all(Color(0x55000000)),
),
onPressed: () {},
child: Text("Forget Password?"),
),
);
}
}
SignUpScreen.dart
class SignUp extends StatefulWidget {
const SignUp({Key? key}) : super(key: key);
#override
_SignUpState createState() => _SignUpState();
}
class _SignUpState extends State<SignUp> {
final _formKey = GlobalKey<FormState>();
final _usernameController = TextEditingController(text: "hammad11"),
_emailController = TextEditingController(text: "mason#gmail.com"),
_passwordController = TextEditingController(text: "ha11"),
_confirmPassController = TextEditingController(text: "ha11");
_onValidate(String? value, ValidationType type) {
switch (type) {
case ValidationType.username:
if (value!.isNotEmpty && value.length < 8)
return "Username Must Be 8 characters long";
else if (value.isEmpty) return "Username required";
return null;
case ValidationType.email:
if (value!.isEmpty)
return "Email required";
else if (!value.isEmail) return "Please enter a Valid Email";
return null;
case ValidationType.password:
if (value!.isEmpty)
return "Password required";
else if (value.isAlphabetOnly || value.isNumericOnly)
return "Password must be AlphaNumeric";
return null;
case ValidationType.confirmPassword:
if (value!.isEmpty)
return "Confirm Password required";
else if (value != _passwordController.text)
return "Password doesn't match";
return null;
}
}
_onRegister() async {
//Clears any snackbar opened due to Error or Multiple clicks
//ScaffoldMessenger.of(context).clearSnackBars();
log("SignUp -> _onRegisterTap ");
if (_formKey.currentState!.validate()) {
WidgetUtils.showLoaderIndicator(context, "Please wait! Loading.....");
final isLoggedIn = await context.read(loginPod).register(
_usernameController.text,
_emailController.text,
_passwordController.text,
);
await Beamer.of(context).popRoute();
if (isLoggedIn) Beamer.of(context).beamToNamed(Routes.login);
} else
log("Form Input Invalid");
}
_onChanged(_, UserAuthService service) async {
if (service.errorMsg != null) WidgetUtils.snackBar(_, service.errorMsg!);
// if (!service.isLoading) await Beamer.of(context).popRoute();
// if (service.taskCompleted) {
// log("User Added Successfully");
// Beamer.of(context).popToNamed(
// Routes.login_id_pass,
// replaceCurrent: true,
// );
// } else {
// WidgetUtils.snackBar(context, service.errorMsg!);
// }
}
_alreadyHaveAccount() => Beamer.of(context).popToNamed(Routes.main);
#override
Widget build(BuildContext context) {
return ProviderListener(
provider: loginPod,
onChange: _onChanged,
child: Scaffold(
body: LayoutBuilder(builder: (context, constraints) {
return SingleChildScrollView(
child: SizedBox(
height: 1.sh,
child: Column(
children: [
_buildSpacer(50),
BoldHeadingWidget(heading: "Sign Up"),
CustomForm(
child: Column(
children: [
CustomTextFieldWithLabeled(
label: "Username",
hintText: "Type Username",
onValidate: (value) =>
_onValidate(value, ValidationType.username),
controller: _usernameController,
icon: CupertinoIcons.person),
CustomTextFieldWithLabeled(
label: "Email",
hintText: "Type Email",
controller: _emailController,
onValidate: (value) =>
_onValidate(value, ValidationType.email),
icon: CupertinoIcons.envelope),
CustomTextFieldWithLabeled(
label: "Password",
hintText: "Type Password",
controller: _passwordController,
onValidate: (value) =>
_onValidate(value, ValidationType.password),
icon: CupertinoIcons.lock),
CustomTextFieldWithLabeled(
label: "Confirm Password",
hintText: "Type Confirm Password",
onValidate: (value) => _onValidate(
value, ValidationType.confirmPassword),
controller: _confirmPassController,
icon: CupertinoIcons.lock),
Row(
children: [
Checkbox(
value: true,
onChanged: (value) {},
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
),
Flexible(
child: Text(
"I accept Terms & Conditions and the Privacy Policy",
style: TextStyle(fontSize: 13.sp),
),
),
],
),
_buildSpacer(10),
DefaultElevatedButton(
title: "Sign Up",
onPressed: _onRegister,
),
],
),
formKey: _formKey,
),
const SizedBox(height: 10),
CustomTextButton(
onPressed: () => Beamer.of(context)
.popToNamed(Routes.login, stacked: false),
title: AppStrings.alreadyHaveAccount,
),
// Spacer(flex: 2),
],
),
),
);
}),
),
);
}
ResponsiveVrtSpacer _buildSpacer(double value) =>
ResponsiveVrtSpacer(space: value.h);
}
showsnackbar(String message, context) {
final snackbar = SnackBar(
content: Text(message),
duration: Duration(seconds: 2),
);
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(snackbar);
}
Use this function to call snackbar it will remove the current snackbar and will only show the latest.
I'm trying to make an app that has basically the same mechanics as a simple todo-app. My problem is, when I try to open the screen where I can create a new project/todo(new_project_screen), there should be loaded some TextFields, but they don't. Instead, this error occurs. I tried several solutions from stackoverflow, but nothing worked and I have no idea why it's not working.(sorry for my bad english, I'm not a native xD)
Here is my code:
main.dart:
import 'package:flutter/material.dart';
import 'package:leisy/surface/main_screen.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(App());
}
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Leisy',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MainScreen(),
);
}
}
main_screen.dart:
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:sqflite/sqflite.dart';
import 'package:leisy/db/database_helper.dart';
import 'package:leisy/model/project.dart';
import 'package:leisy/surface/settings_screen.dart';
import 'package:leisy/surface/new_project_screen.dart';
class MainScreen extends StatefulWidget {
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
DatabaseHelper databaseHelper = DatabaseHelper();
List<Project> projectList = [];
int count = 0;
#override
Widget build(BuildContext context) {
if (projectList == null) {
projectList = <Project>[];
updateListView();
}
debugPrint("Building entire main screen scaffold");
return Scaffold(
appBar: AppBar(
title: Text("Leisy"),
),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Center(
child: Text(
"Menü",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 25,
),
),
),
decoration: BoxDecoration(color: Colors.blue),
),
ListTile(
leading: Icon(Icons.home),
title: Text('Home'),
onTap: () => {Navigator.of(context).pop()},
),
ListTile(
leading: Icon(Icons.add),
title: Text('Neues Projekt'),
onTap: () => {
Navigator.of(context).push(new MaterialPageRoute(
builder: (context) => new NewProjectScreen()))
},
),
ListTile(
leading: Icon(Icons.settings),
title: Text('Einstellungen'),
onTap: () => {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => SettingsScreen()))
},
)
],
),
),
body: getProjectListView(),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: navigateToNewProject,
),
);
}
ListView getProjectListView() {
TextStyle titleStyle = Theme.of(context).textTheme.subtitle1;
return ListView.builder(
itemCount: count,
itemBuilder: (BuildContext context, int position) {
return Card(
color: Colors.white,
elevation: 2.0,
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.blue,
),
title: Text(
this.projectList[position].name,
style: titleStyle,
),
subtitle: Text(this.projectList[position].date),
onTap: () {
debugPrint('ListTile Tapped');
//navigateToDetail() einfügen
},
),
);
});
}
void _showSnackBar(BuildContext context, String message) {
final snackBar = SnackBar(
content: Text(message),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
void navigateToNewProject() async {
bool result = await Navigator.push(context, MaterialPageRoute(builder: (context) => NewProjectScreen()));
if (result == true) {
updateListView();
}
}
void updateListView() {
final Future<Database> dbFuture = databaseHelper.initDB();
dbFuture.then((database) {
Future<List<Project>> projectListFuture = databaseHelper.getProjectList();
projectListFuture.then((projectList) {
setState(() {
this.projectList = projectList;
this.count = projectList.length;
});
});
});
}
}
new_project_screen.dart:
import 'package:flutter/material.dart';
import 'package:leisy/db/database_helper.dart';
import 'package:leisy/model/project.dart';
import 'package:leisy/surface/main_screen.dart';
import 'package:leisy/surface/settings_screen.dart';
class NewProjectScreen extends StatefulWidget {
#override
_NewProjectScreenState createState() => _NewProjectScreenState();
}
class _NewProjectScreenState extends State<NewProjectScreen> {
Project project;
DatabaseHelper helper = DatabaseHelper();
TextEditingController nameController = TextEditingController();
TextEditingController dateController = TextEditingController();
TextEditingController locationController = TextEditingController();
#override
Widget build(BuildContext context) {
nameController.text = project.name;
dateController.text = project.date;
locationController.text = project.location;
return Scaffold(
appBar: AppBar(
title: Text("Neues Projekt"),
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
Navigator.pop(context);
},
),
),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Center(
child: Text(
"Menü",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 25,
),
),
),
decoration: BoxDecoration(color: Colors.blue),
),
ListTile(
leading: Icon(Icons.home),
title: Text('Home'),
onTap: () => {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => MainScreen()))
},
),
ListTile(
leading: Icon(Icons.add),
title: Text('Neues Projekt'),
onTap: () => {Navigator.of(context).pop()},
),
ListTile(
leading: Icon(Icons.settings),
title: Text('Einstellungen'),
onTap: () => {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => SettingsScreen()))
},
)
],
),
),
body: Padding(
padding: EdgeInsets.all(8.0),
child: ListView(
children: <Widget>[
TextField(
controller: nameController,
decoration: InputDecoration(labelText: 'Name'),
style: TextStyle(fontSize: 20),
onChanged: (value) {
setState(() {
updateName();
});
},
),
TextField(
decoration: InputDecoration(labelText: 'Datum'),
style: TextStyle(fontSize: 20),
onChanged: (value) {
setState(() {
updateDate();
});
},
),
TextField(
decoration: InputDecoration(labelText: 'Ort'),
style: TextStyle(fontSize: 20),
onChanged: (value) {
setState(() {
updateLocation();
});
},
),
Row(
children: <Widget>[
Expanded(
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Theme.of(context).primaryColorDark),
),
child: Text('Speichern'),
onPressed: () {
setState(() {
_save();
});
},
)),
Expanded(
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Theme.of(context).primaryColorDark),
),
child: Text('Abbrechen'),
onPressed: () {
setState(() {
_cancel();
});
},
)),
],
)
],
)
)
);
}
void _save() async {
Navigator.pop(context, true);
await helper.insertProject(project);
}
void _showSnackBar(String message) {
SnackBar snackBar = SnackBar(content: Text(message));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
void _cancel() async {
Navigator.pop(context, true);
_showSnackBar('Vorgang abgebrochen');
}
void updateName() {
project.name = nameController.text;
}
void updateDate() {
project.date = dateController.text;
}
void updateLocation() {
project.location = locationController.text;
}
}
project.dart:
class Project {
int _id;
String _name;
String _date;
String _location;
String _personaldb;
Project(this._name, this._date, this._location, this._personaldb);
Project.withId(
this._id, this._name, this._date, this._location, this._personaldb);
int get id => _id;
String get name => _name;
String get date => _date;
String get location => _location;
String get personalDB => _personaldb;
set name(String newName) {
if (newName.length <= 63) {
this._name = newName;
}
}
set date(String newDate) {
this._date = newDate;
}
set location(String newLocation) {
this._location = newLocation;
}
set personalDB(String newPersonalDB) {
this._personaldb = newPersonalDB;
}
Map<String, dynamic> toMap() {
var map = Map<String, dynamic>();
if (id != null) {
map['id'] = _id;
}
map['name'] = _name;
map['date'] = _date;
map['location'] = _location;
map['personalDB'] = _personaldb;
return map;
}
Project.fromMapObject(Map<String, dynamic> map) {
this._id = map['id'];
this._name = map['name'];
this._date = map['date'];
this._location = map['location'];
this._personaldb = map['personalDB'];
}
}
database_helper.dart:
import 'dart:async';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:leisy/model/project.dart';
class DatabaseHelper {
static DatabaseHelper _databaseHelper;
static Database _database;
String projectTable = 'project_table';
String colId = 'id';
String colName = 'name';
String colDate = 'date';
String colLocation = 'location';
String colPersonalDB = 'personalDB';
DatabaseHelper._createInstance();
factory DatabaseHelper() {
if (_databaseHelper == null) {
_databaseHelper = DatabaseHelper._createInstance();
}
return _databaseHelper;
}
Future<Database> get database async {
if (_database == null) {
_database = await initDB();
}
return _database;
}
Future<Database> initDB() async {
Directory directory = await getApplicationDocumentsDirectory();
String path = directory.path + 'projects.db';
var projectsDB = await openDatabase(path, version: 1, onCreate: _createDB);
return projectsDB;
}
void _createDB(Database db, int newVersion) async {
await db.execute('CREATE TABLE $projectTable('
'$colId INTEGER PRIMARY KEY AUTOINCREMENT,'
'$colName TEXT,'
'$colDate TEXT,'
'$colLocation TEXT,'
'$colPersonalDB TEXT)');
}
Future<List<Map<String, dynamic>>> getProjectMapList() async {
Database db = await this.database;
var result = await db.query(projectTable);
return result;
}
Future<int> insertProject(Project project) async {
Database db = await this.database;
var result = await db.insert(projectTable, project.toMap());
return result;
}
Future<int> updateProject(Project project) async {
var db = await this.database;
var result = await db.update(projectTable, project.toMap(), where: '$colId = ?', whereArgs: [project.id]);
return result;
}
Future<int> deleteProject(int id) async {
var db = await this.database;
int result = await db.delete(projectTable, where: '$colId = $id');
return result;
}
Future<int> getCount() async {
Database db = await this.database;
List<Map<String, dynamic>> x = await db.rawQuery('SELECT COUNT (*) from $projectTable');
int result = Sqflite.firstIntValue(x);
return result;
}
Future<List<Project>> getProjectList() async {
var projectMapList = await getProjectMapList();
int count = projectMapList.length;
List<Project> projectList = <Project>[];
for (int i = 0; i < count; i++) {
projectList.add(Project.fromMapObject(projectMapList[i]));
}
return projectList;
}
}
Error:
======== Exception caught by widgets library =======================================================
The following NoSuchMethodError was thrown building NewProjectScreen(dirty, state: _NewProjectScreenState#670bc):
The getter 'name' was called on null.
Receiver: null
Tried calling: name
The relevant error-causing widget was:
NewProjectScreen file:///C:/Users/gabri/AndroidStudioProjects/leisy/lib/surface/main_screen.dart:116:89
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:54:5)
#1 _NewProjectScreenState.build (package:leisy/surface/new_project_screen.dart:24:35)
#2 StatefulElement.build (package:flutter/src/widgets/framework.dart:4612:27)
#3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4495:15)
#4 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4667:11)
...
====================================================================================================
If anybody can help me, I would really appreciate it. And if anything in my code is inconvenient or badly implemented, please be kind with me I'm a beginner at this.
The file new_project_screen.dart has the problem
To be exact, the problem can be found in lines 24:35
And seems like that project variable is null project.name;
That is because you are creating Project project but isn't instancied yet. Is just a null variable
See the image below, will help you to understand a bit:
https://dartpad.dev/1ebc897cd3f547cc0b79e52290a63653
So to fix it, you need to create the instance previously
I'm learning Flutter and I'm trying to use the SQFLite package to persist data on the device. Everything works perfect but there's one problem, every time I add a new element or I upgrade it, I need to restart my app to see the changes and I don't know why, there are no errors or anything.
I uploaded it to github so you can try it, maybe is something in my emulator or something I dont know.
https://github.com/Rodrigogzmn6/sql_demo
or you can see part of the complete project here
Home Page
import 'package:flutter/material.dart';
import 'package:sql_demo/models/product.dart';
import 'package:sql_demo/pages/add_product_page.dart';
import 'package:sql_demo/pages/update_product_page.dart';
import 'package:sql_demo/pages/view_product_page.dart';
import 'package:sql_demo/services/dbhelper.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
DbHelper helper;
#override
void initState() {
super.initState();
helper = DbHelper();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Page'),
),
body: FutureBuilder(
future: helper.getProducts(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
Product product = Product.fromMap(snapshot.data[index]);
return ListTile(
contentPadding: const EdgeInsets.all(16.0),
title: Text(
'${product.name} - \$${(product.price).toString()}'),
subtitle: Text(product.description),
trailing: Column(
children: [
Expanded(
child: IconButton(
icon: Icon(
Icons.delete,
color: Colors.red,
),
onPressed: () {
setState(() {
helper.deleteProduct(product.id);
});
}),
),
SizedBox(
height: 20.0,
),
Expanded(
child: IconButton(
icon: Icon(
Icons.edit,
color: Colors.blue,
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UpdateProductPage(
product: product,
)),
);
}),
),
],
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductDetailsPage(
product: product,
)));
},
);
});
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
floatingActionButton: FloatingActionButton(
child: Icon(
Icons.add,
color: Colors.white,
),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => AddProductPage()));
}),
);
}
}
Creating Item page
import 'package:flutter/material.dart';
import 'package:sql_demo/models/product.dart';
import 'package:sql_demo/services/dbhelper.dart';
class AddProductPage extends StatefulWidget {
#override
_AddProductPageState createState() => _AddProductPageState();
}
class _AddProductPageState extends State<AddProductPage> {
String name, description;
num price;
DbHelper helper;
//Instantiate Helper
#override
void initState() {
super.initState();
helper = DbHelper();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Add new product"),
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
TextFormField(
decoration: InputDecoration(
hintText: 'Enter product name',
labelText: 'Product name',
),
onChanged: (value) {
setState(() {
name = value;
});
},
),
SizedBox(height: 16),
TextFormField(
decoration: InputDecoration(
hintText: 'Enter product description',
labelText: 'Product description',
),
onChanged: (value) {
setState(() {
description = value;
});
},
),
SizedBox(height: 16),
TextFormField(
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: 'Enter product price',
labelText: 'Product price',
),
onChanged: (value) {
setState(() {
price = double.parse(value);
});
},
),
SizedBox(height: 16),
RaisedButton(
child: Text('Save'),
onPressed: () {
Product product = Product({
'name': name,
'description': description,
'price': price,
});
setState(() {
helper.createProduct(product);
Navigator.pop(context);
});
},
),
],
),
);
}
}
Database operations
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:sql_demo/models/product.dart';
class DbHelper {
//Creates a singleton class (it only creates once)
static final DbHelper _singleton = DbHelper._internal();
factory DbHelper() => _singleton;
DbHelper._internal();
//Creating an object of type database
static Database _database;
//Initializing or creating the database named mydb.db
//Creating a products table with necessary fields
Future<Database> initDatabase() async {
//If this class was never instaciated, we will execute this method, otherwise return
if (_database != null) return _database;
String path = join(await getDatabasesPath(), 'mydb.db');
_database = await openDatabase(
path,
version: 1,
onCreate: (Database db, int v) async {
await db.execute(
'CREATE TABLE Products (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, description TEXT, price REAL)');
},
);
return _database;
}
//Select all products
Future<List> getProducts() async {
Database db = await initDatabase();
return await db.query('Products');
}
//Create new product
Future<int> createProduct(Product product) async {
Database db = await initDatabase();
print('Product added');
return await db.insert(
'Products',
product.toMap(),
);
}
//Update product
Future<int> updateProduct(Product product) async {
Database db = await initDatabase();
return await db.update(
'Products',
product.toMap(),
where: 'id=?',
whereArgs: [product.id],
);
}
//Delete a product
Future<int> deleteProduct(int productID) async {
Database db = await initDatabase();
return await db.delete('Products', where: 'id=?', whereArgs: [productID]);
}
}
You can copy paste run full code below
You can await Navigator.push to AddProductPage and then call setState(() {});
code snippet
onPressed: () async {
await Navigator.push(context,
MaterialPageRoute(builder: (context) => AddProductPage()));
setState(() {});
}),
working demo
full code
import 'package:flutter/material.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'dart:convert';
class Product {
int _id;
String _name;
String _description;
double _price;
//Getters because the attributes are private
int get id => _id;
String get name => _name;
String get description => _description;
double get price => _price;
//Constructor
Product(dynamic obj) {
_id = obj['id'];
_name = obj['name'];
_description = obj['description'];
_price = obj['price'];
}
//Named objet (takes a Map as parameter)
Product.fromMap(Map<String, dynamic> data) {
_id = data['id'];
_name = data['name'];
_description = data['description'];
_price = data['price'];
}
//Method that converts the object into a map
Map<String, dynamic> toMap() {
return {
'id': _id,
'name': _name,
'description': _description,
'price': _price,
};
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
DbHelper helper;
#override
void initState() {
super.initState();
helper = DbHelper();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Page'),
),
body: FutureBuilder(
future: helper.getProducts(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
Product product = Product.fromMap(snapshot.data[index]);
return ListTile(
contentPadding: const EdgeInsets.all(16.0),
title: Text(
'${product.name} - \$${(product.price).toString()}'),
subtitle: Text(product.description),
trailing: Column(
children: [
Expanded(
child: IconButton(
icon: Icon(
Icons.delete,
color: Colors.red,
),
onPressed: () {
setState(() {
helper.deleteProduct(product.id);
});
}),
),
SizedBox(
height: 20.0,
),
Expanded(
child: IconButton(
icon: Icon(
Icons.edit,
color: Colors.blue,
),
onPressed: () {
/*Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UpdateProductPage(
product: product,
)),
);*/
}),
),
],
),
onTap: () {
/*Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductDetailsPage(
product: product,
)));*/
},
);
});
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
floatingActionButton: FloatingActionButton(
child: Icon(
Icons.add,
color: Colors.white,
),
onPressed: () async {
await Navigator.push(context,
MaterialPageRoute(builder: (context) => AddProductPage()));
setState(() {});
}),
);
}
}
class AddProductPage extends StatefulWidget {
#override
_AddProductPageState createState() => _AddProductPageState();
}
class _AddProductPageState extends State<AddProductPage> {
String name, description;
num price;
DbHelper helper;
//Instantiate Helper
#override
void initState() {
super.initState();
helper = DbHelper();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Add new product"),
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
TextFormField(
decoration: InputDecoration(
hintText: 'Enter product name',
labelText: 'Product name',
),
onChanged: (value) {
setState(() {
name = value;
});
},
),
SizedBox(height: 16),
TextFormField(
decoration: InputDecoration(
hintText: 'Enter product description',
labelText: 'Product description',
),
onChanged: (value) {
setState(() {
description = value;
});
},
),
SizedBox(height: 16),
TextFormField(
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: 'Enter product price',
labelText: 'Product price',
),
onChanged: (value) {
setState(() {
price = double.parse(value);
});
},
),
SizedBox(height: 16),
RaisedButton(
child: Text('Save'),
onPressed: () {
Product product = Product({
'name': name,
'description': description,
'price': price,
});
setState(() {
helper.createProduct(product);
Navigator.pop(context);
});
},
),
],
),
);
}
}
class DbHelper {
//Creates a singleton class (it only creates once)
static final DbHelper _singleton = DbHelper._internal();
factory DbHelper() => _singleton;
DbHelper._internal();
//Creating an object of type database
static Database _database;
//Initializing or creating the database named mydb.db
//Creating a products table with necessary fields
Future<Database> initDatabase() async {
//If this class was never instaciated, we will execute this method, otherwise return
if (_database != null) return _database;
String path = join(await getDatabasesPath(), 'mydb.db');
_database = await openDatabase(
path,
version: 1,
onCreate: (Database db, int v) async {
await db.execute(
'CREATE TABLE Products (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, description TEXT, price REAL)');
},
);
return _database;
}
//Select all products
Future<List> getProducts() async {
Database db = await initDatabase();
return await db.query('Products');
}
//Create new product
Future<int> createProduct(Product product) async {
Database db = await initDatabase();
print('Product added');
return await db.insert(
'Products',
product.toMap(),
);
}
//Update product
Future<int> updateProduct(Product product) async {
Database db = await initDatabase();
return await db.update(
'Products',
product.toMap(),
where: 'id=?',
whereArgs: [product.id],
);
}
//Delete a product
Future<int> deleteProduct(int productID) async {
Database db = await initDatabase();
return await db.delete('Products', where: 'id=?', whereArgs: [productID]);
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: HomePage(),
);
}
}
Hot reload: Hot reload feature quickly compile the newly added code in our file and sent the code to Dart Virtual Machine. If you are using States in your application then Hot Reload preservers the States so they will not update on Hot Reload our set to their default values.
And, initState() does not rebuild after hot reload as its state is preserved. So, you have to perform hot restart to see database changes:
#override
void initState() {
super.initState();
helper = DbHelper();
}
When stateful widget is build, you have helper, but after you hot reload, the helper will be same as initState only called hot restart or you launch the widget. So, helper variable is preserved. Hence, you have to hot restart.
Links that might help you: Understanding Flutter hot reload and hot restart the simplest way | Refs