Flutter initialize data into a table at startup - database

I can't seem to find how to write a constant data into a database to be loaded every start up. I want to write a set of data into the specializeArea table where it has an id column and area column as seen here from the DbHelper.dart and specializeModel.dart:
import 'package:conferenceApp/Model/UserModel.dart';
import 'package:conferenceApp/Model/specializeModel.dart';
import 'package:conferenceApp/Model/loginModel.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'dart:io' as io;
class DbHelper {
static Database conference;
static const String DB_Name = 'conference.db';
static const String Table_conferenceInfo = 'conferenceinfo';
static const String Table_specializeArea = 'specializearea';
static const String Table_login = 'login';
static const String C_UserID = 'id';
static const String C_Name = 'name';
static const String C_UName = 'username';
static const String C_Email = 'email';
static const String C_Phone = 'phone';
static const String C_Role = 'role';
static const String C_Area = 'area';
static const String C_Password = 'password';
static const int Version = 1;
Future<Database> get db async {
if (conference != null) {
return conference;
}
conference = await initDb();
return conference;
}
initDb() async {
io.Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, DB_Name);
var db = await openDatabase(path, version: Version, onCreate: _onCreate);
return db;
}
_onCreate(Database db, int intVersion) async {
await db.execute("CREATE TABLE $Table_conferenceInfo ("
" $C_UserID INTEGER, "
" $C_Name TEXT, "
" $C_UName TEXT, "
" $C_Email TEXT,"
" $C_Password TEXT, "
" $C_Phone INTEGER, "
" $C_Role TEXT, "
" PRIMARY KEY ($C_UserID)"
")");
await db.execute("CREATE TABLE $Table_specializeArea ("
" $C_UserID INTEGER, "
" $C_Area TEXT )");
await db.execute("CREATE TABLE $Table_login ("
" $C_UserID INTEGER, "
" $C_UName TEXT, "
" $C_Password TEXT )");
}
Future<int> saveData(UserModel user) async {
var dbClient = await db;
var res = await dbClient.insert(Table_conferenceInfo, user.toMap());
return res;
}
Future<int> saveData1(specializeModel user) async {
var dbClient = await db;
var res1 = await dbClient.insert(Table_specializeArea, user.toMap());
return res1;
}
Future<int> saveData2(loginModel user) async {
var dbClient = await db;
var res2 = await dbClient.insert(Table_login, user.toMap());
return res2;
}
Future<UserModel> getLoginUser(String userId, String password) async {
var dbClient = await db;
var res =
await dbClient.rawQuery("SELECT * FROM $Table_conferenceInfo WHERE "
"$C_UName = '$userId' AND "
"$C_Password = '$password'");
if (res.length > 0) {
return UserModel.fromMap(res.first);
}
return null;
}
Future<specializeModel> getSubject(String subject) async {
var dbClient = await db;
var res =
await dbClient.rawQuery("SELECT * FROM $Table_specializeArea WHERE "
"$C_Area = '$subject'");
if (res.length > 0) {
return specializeModel.fromMap(res.first);
}
return null;
}
Future<int> updateUser(UserModel user) async {
var dbClient = await db;
var res = await dbClient.update(Table_conferenceInfo, user.toMap(),
where: '$C_UserID = ?', whereArgs: [user.user_id]);
return res;
}
Future<int> deleteUser(String user_id) async {
var dbClient = await db;
var res = await dbClient.delete(Table_conferenceInfo,
where: '$C_UserID = ?', whereArgs: [user_id]);
return res;
}
}
class specializeModel {
String user_id;
String user_area;
specializeModel(this.user_id, this.user_area);
Map<String, dynamic> toMap() {
var map = <String, dynamic>{
'id': user_id,
'user_area': user_area,
};
return map;
}
specializeModel.fromMap(Map<String, dynamic> map) {
user_id = map['id'];
user_area = map['user_area'];
}
#override
String toString() {
return 'specializeModel{id: $user_id, user_area: $user_area}';
}
}
The data I want to load is around 5 sets.
Here is the main page of the app, HomeForm.dart:
import 'package:conferenceApp/Model/specializeModel.dart';
import 'package:flutter/material.dart';
import 'package:conferenceApp/Comm/comHelper.dart';
import 'package:conferenceApp/Comm/genTextFormField.dart';
import 'package:conferenceApp/DatabaseHandler/DbHelper.dart';
import 'package:conferenceApp/Model/UserModel.dart';
import 'package:conferenceApp/Screens/LoginForm.dart';
import 'package:shared_preferences/shared_preferences.dart';
class HomeForm extends StatefulWidget {
#override
_HomeFormState createState() => _HomeFormState();
}
class _HomeFormState extends State<HomeForm> {
final _formKey = new GlobalKey<FormState>();
Future<SharedPreferences> _pref = SharedPreferences.getInstance();
DbHelper dbHelper;
final _conUserId = TextEditingController();
final _conName = TextEditingController();
final _conDelUserId = TextEditingController();
final _conUserName = TextEditingController();
final _conEmail = TextEditingController();
final _conPassword = TextEditingController();
final _conCPhone = TextEditingController();
final _conRole = TextEditingController();
#override
void initState() {
super.initState();
getUserData();
dbHelper = DbHelper();
}
Future<void> getUserData() async {
final SharedPreferences sp = await _pref;
setState(() {
_conUserId.text = sp.getString("id");
_conDelUserId.text = sp.getString("id");
_conName.text = sp.getString("name");
_conUserName.text = sp.getString("username");
_conEmail.text = sp.getString("email");
_conPassword.text = sp.getString("password");
_conCPhone.text = sp.getString("phone");
_conRole.text = sp.getString("role");
});
}
update() async {
String uid = _conUserId.text;
String Name = _conName.text;
String uname = _conUserName.text;
String email = _conEmail.text;
String passwd = _conPassword.text;
String phone = _conCPhone.text;
String role = _conRole.text;
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
UserModel user = UserModel(uid, Name, uname, email, passwd, phone, role);
await dbHelper.updateUser(user).then((value) {
if (value == 1) {
alertDialog(context, "Successfully Updated");
updateSP(user, true).whenComplete(() {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (_) => LoginForm()),
(Route<dynamic> route) => false);
});
} else {
alertDialog(context, "Error Update");
}
}).catchError((error) {
print(error);
alertDialog(context, "Error");
});
}
}
delete() async {
String delUserID = _conDelUserId.text;
await dbHelper.deleteUser(delUserID).then((value) {
if (value == 1) {
alertDialog(context, "Successfully Deleted");
updateSP(null, false).whenComplete(() {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (_) => LoginForm()),
(Route<dynamic> route) => false);
});
}
});
}
Future updateSP(UserModel user, bool add) async {
final SharedPreferences sp = await _pref;
if (add) {
sp.setString("username", user.user_name);
sp.setString("Name", user.Name);
sp.setString("email", user.email);
sp.setString("password", user.password);
sp.setString("phone", user.phone);
sp.setString("role", user.role);
} else {
sp.remove('id');
sp.remove('name');
sp.remove('username');
sp.remove('email');
sp.remove('password');
sp.remove('phone');
sp.remove('role');
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home'),
),
body: Form(
key: _formKey,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Container(
margin: EdgeInsets.only(top: 20.0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
//Update
getTextFormField(
controller: _conUserId,
isEnable: false,
icon: Icons.person,
hintName: 'User ID'),
SizedBox(height: 10.0),
getTextFormField(
controller: _conName,
icon: Icons.person,
hintName: 'Name'),
SizedBox(height: 10.0),
getTextFormField(
controller: _conUserName,
icon: Icons.person_outline,
inputType: TextInputType.name,
hintName: 'Username'),
SizedBox(height: 10.0),
getTextFormField(
controller: _conEmail,
icon: Icons.email,
inputType: TextInputType.emailAddress,
hintName: 'Email'),
SizedBox(height: 10.0),
getTextFormField(
controller: _conPassword,
icon: Icons.lock,
hintName: 'Password',
isObscureText: true,
),
SizedBox(height: 10.0),
getTextFormField(
controller: _conCPhone,
icon: Icons.phone,
hintName: 'Phone',
),
SizedBox(height: 10.0),
getTextFormField(
controller: _conRole,
icon: Icons.remove_red_eye,
hintName: 'Role',
),
SizedBox(height: 10.0),
Container(
margin: EdgeInsets.all(30.0),
width: double.infinity,
child: FlatButton(
child: Text(
'Update',
style: TextStyle(color: Colors.white),
),
onPressed: update,
),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(30.0),
),
),
//Specialization area
//Delete
getTextFormField(
controller: _conDelUserId,
isEnable: false,
icon: Icons.person,
hintName: 'User ID'),
SizedBox(height: 10.0),
SizedBox(height: 10.0),
Container(
margin: EdgeInsets.all(30.0),
width: double.infinity,
child: FlatButton(
child: Text(
'Delete',
style: TextStyle(color: Colors.white),
),
onPressed: delete,
),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(30.0),
),
),
],
),
),
),
),
),
);
}
}
If possible, I would also like to display the data stored as buttons or interactables later but storing them comes first.
Thank you

Related

Not saving the flutter switch value to sqflite database

I am a completely a beginner to sqlite and flutter. I was trying to create a local database to my flutter to do app. So I watched some youtube videos and started to implementing a database using flutter sqflite plugin. Everything worked fine because all I did was copy typing the you tubers code, until I had to add an extra parameter to the code which is a boolean, in order to track the status of the task (like done the task or not). I used an int value to save the bool, while sqlite does not supports boolean values. I used two functions, one to update the text and the other to update the switch value.
And secondly when I tap on a switch it triggers all the switches in the list. I want to solve that issue as well.
Model class for the Task
class Tasksdb {
final int? id;
final String taskName;
bool isDone;
Tasksdb({
this.id,
required this.taskName,
required this.isDone,
});
factory Tasksdb.fromMap(Map<String, dynamic> json) => Tasksdb(
id: json['id'],
taskName: json['taskName'],
isDone: (json['isDone'] as int) == 0 ? false : true);
Map<String, dynamic> toMap() {
return {
'id': id,
'taskName': taskName,
'isDone': isDone,
};
}
}
DatabaseHelper class
class DatabaseHelper {
DatabaseHelper._privateConstructor();
static final DatabaseHelper instance = DatabaseHelper._privateConstructor();
static Database? _database;
Future<Database> get database async => _database ??= await _initDatabase();
Future<Database> _initDatabase() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, 'tasks.db');
return await openDatabase(
path,
version: 1,
onCreate: _onCreate,
);
}
Future _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE IF NOT EXISTS "taskstable" (
"id" INTEGER,
"taskName" TEXT,
"isDone" INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY("id" AUTOINCREMENT)
);
''');
}
Future<List<Tasksdb>> getTasks() async {
Database db = await instance.database;
var tasksQuery = await db.query(
'taskstable',
);
List<Tasksdb> taskList = tasksQuery.isNotEmpty
? tasksQuery.map((c) => Tasksdb.fromMap(c)).toList()
: [];
return taskList;
}
Future<int> add(Tasksdb task) async {
Database db = await instance.database;
return await db.insert('taskstable', {
'id': task.id,
'taskName': task.taskName,
'isDone': 0,
});
}
Future<int> update(Tasksdb task) async {
Database db = await instance.database;
return await db.update(
'taskstable',
task.toMap(),
where: "id = ?",
whereArgs: [task.id],
);
}
Future<int> updateIsDone(bool isDoneTodb) async {
Database db = await instance.database;
return await db.update(
'taskstable',
{
'isDone': isDoneTodb == true ? 1 : 0,
},
);
}
}
HomeScreen widget
class SqliteApp extends StatefulWidget {
const SqliteApp({Key? key}) : super(key: key);
#override
_SqliteAppState createState() => _SqliteAppState();
}
class _SqliteAppState extends State<SqliteApp> {
int? selectedId;
final textController = TextEditingController();
bool isDone = false;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: TextField(
controller: textController,
),
),
body: Center(
child: FutureBuilder<List<Tasksdb>>(
future: DatabaseHelper.instance.getTasks(),
builder: (BuildContext context,
AsyncSnapshot<List<Tasksdb>> snapshot) {
if (!snapshot.hasData) {
return const Center(child: Text('Loading...'));
}
return snapshot.data!.isEmpty
? const Center(child: Text('No tasks in List.'))
: ListView(
children: snapshot.data!.map((task) {
return Center(
child: Card(
color: selectedId == task.id
? Colors.green
: Colors.yellow,
child: ListTile(
trailing: Switch( //the problem is here, doesn't save to db
value: isDone,
onChanged: (val) async {
setState(() {
isDone = val;
});
await DatabaseHelper.instance
.updateIsDone(
isDone,
);
}),
title: Text(task.taskName),
onTap: () {
setState(() {
if (selectedId == null) {
textController.text = task.taskName;
selectedId = task.id;
} else {
textController.text = 'add something';
selectedId = null;
}
});
},
),
),
);
}).toList(),
);
}),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
await DatabaseHelper.instance.add(
Tasksdb(
taskName: textController.text,
isDone: false,
),
);
setState(() {
textController.clear();
selectedId = null;
});
},
),
),
);
}
}
I found the answer: (in case of if someone had the same question)
I removed the bool isDone from the material app widget, and instead of assigning the switch val to that bool I assigned it to database's task.isDone value. To avoid switch's auto trigger, I parsed the Taskdb to the updateIsDone function
Future<int> updateIsDone(Tasksdb task, bool isDoneTodb) async {
Database db = await instance.database;
return await db.update(
'taskstable',
{
'isDone': isDoneTodb == true ? 1 : 0,
},
where: "id = ?",
whereArgs: [task.id]);
}
...
Switch(
value: task.isDone,
onChanged: (val) async {
setState(() {
task.isDone = val;
});
await DatabaseHelper.instance
.updateIsDone(task, task.isDone);
});

Flutter/Dart - How to process Json data which contains a comma separated list?

I've got a PageViewBuilder which uses a Map<String> of json data containing a String called name and another called tagname. The problem is that the tagname string is actually a comma-separated list of three tags which print out as "boat,grass,toads"; But I'd like to separate this String so that I can make each item a clickable button. IE:
FlatButton( onPressed: () => boat(),
text: "boat",
);
FlatButton( onPressed: () => grass(),
text: "grass",
);
FlatButton( onPressed: () => toads(),
text: "toads",
);
I tried to using Dart's split function like this;
var splitag = tagname.split(",");
var splitag1 = splitag[0];
var splitag2 = splitag[1];
var splitag3 = splitag[2];
But this gives an error if any of the three tag names are void.
So I tried this;
String splitagone = splitag1 ?? "";
String splitagtwo = splitag2 ?? "";
String splitagthree = splitag3 ?? "";
But this still gave me an error. So can anyone suggest a different way to accomplish what I need? Here's the full code;
class SpeakContent {
final String name, tagname;
SpeakContent({
this.name,
this.tagname
});
factory SpeakContent.fromJson(Map<String, dynamic> jsonData) {
return SpeakContent(
name: jsonData['name'],
tagname: jsonData['tagname'],
);
}
}
class StageBuilder extends StatelessWidget {
final List<SpeakContent> speakcrafts;
StageBuilder(this.speakcrafts);
#override
Widget build(context) {
return PageView.builder(
itemCount: speakcrafts.length,
itemBuilder: (context, int currentIndex) {
return createViewItem(speakcrafts[currentIndex], context);
},
);
}
Widget createViewItem(SpeakContent speakcraft, BuildContext context) {
return Column(
children: <Widget>[
Container(
child: Text(
speakcraft.name,
),
),
Container(child:
FlatButton( onPressed: () => boat(),
child: Text('boat'),
),
),
Container(child:
FlatButton( onPressed: () => grass(),
child: Text('grass'),
),
),
Container(child:
FlatButton( onPressed: () => toads(),
child: Text('toads'),
)
),
],
);
}
}
You can do something like this..
Widget createViewItem(SpeakContent speakcraft, BuildContext context) {
List<Widget> columnChildren = [];
Text name = Text(speakcraft.name);
columnChildren.add(name);
speakcraft.tagname.split(',').forEach((String tag) {
FlatButton tagButton = FlatButton(
child: Text(tag),
onPressed: (){},
);
columnChildren.add(tagButton);
});
return Column(
children: columnChildren,
);
}
You can try:
var response = await http.get(
Uri.encodeFull("...."),
headers: {
...
}
);
this.setState(() {
testList = json.decode (response.body) as List;
**testList[0];
testList[1];**

Displaying Single Document from Firebase

I'm having problems getting data from my firebase. I have the data but do not know how to display it.
Here is my Database:
This is my code:
class Home extends StatefulWidget {
final dynamic userUid, nicked;
Home({this.userUid, this.nicked});
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
final int delayedAmount = 500;
//final AuthService _auth = AuthService();
void getNick() {
//get nickname of this User
final CollectionReference userNickname = Firestore.instance.collection('NickNames of Users');
var documentNick = userNickname.document(widget.userUid);
documentNick.get()
.then((DocumentSnapshot ds){
var nick = ds.data['MyNickName'];
print(nick);
var nicked = Home(nicked: nick,);
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Scaffold(
backgroundColor: Colors.greenAccent[400],
body: SingleChildScrollView(
physics: ScrollPhysics(),
child: Column(
children: <Widget>[
Container(
margin: EdgeInsets.fromLTRB( 0 , 50, 0, 0),
child: Column(
children: <Widget>[
Align(
alignment: Alignment.topLeft,
child: DelayedAnimation(
child:
Text(
widget.nicked,//This i where the firebase data nickName code will go
textScaleFactor : 1,
style: TextStyle(
fontFamily: 'Comic',
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 30,
),
),
So i just dont know how to get the single document from firebase and display it.
So this is how i get userUid...
import 'package:firebase_auth/firebase_auth.dart';
//import 'package:flutter/cupertino.dart';
import 'package:fruithero/model/user.dart';
import 'package:fruithero/services/database.dart';
import 'package:fruithero/pages/myHome.dart';
class AuthService {
final dynamic nickNamesofUser;
AuthService({this.nickNamesofUser});
final FirebaseAuth _auth = FirebaseAuth.instance;
void homeTransporter(uid){
dynamic homeTransport = uid;
Home(userUid: homeTransport,);
}
// create user obj based on firebase user
User _userFromFirebaseUser(FirebaseUser user) {
return user != null ? User(uid: user.uid) : null;
}
// auth change user stream
Stream<User> get user {
return _auth.onAuthStateChanged
//.map((FirebaseUser user) => _userFromFirebaseUser(user));
.map(_userFromFirebaseUser);
}
// sign in anon
Future signInAnon() async {
try {
AuthResult result = await _auth.signInAnonymously();
FirebaseUser user = result.user;
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
// sign in with email and password
Future signInWithEmailAndPassword(String email, String password) async {
try {
AuthResult result = await _auth.signInWithEmailAndPassword(email: email, password: password);
FirebaseUser user = result.user;
return user;
} catch (error) {
print(error.toString());
return null;
}
}
// register with email and password
Future registerWithEmailAndPassword(String email, String password) async {
try {
AuthResult result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
FirebaseUser user = result.user;
//storing user uid
homeTransporter(user.uid);
//create new document for user with the uid
await DataBaseServices(uid: user.uid).createDocumentWithUid(nickNamesofUser);
//await DatabaseService(uid: user.uid).updateUserData(nickNamesofUser);
return _userFromFirebaseUser(user);
} catch (error) {
return null;
}
}
// sign out
Future signOut() async {
try {
return await _auth.signOut();
} catch (error) {
print(error.toString());
return null;
}
}
}
I hope this helps. Sir..............................................................................................................................................................
String nick,userID;
void initState() {
super.initState();
userID = widget.uid;
getNick();
print(userID);
}
void getNick() {
var users;
//get nickname of this User
Firestore.instance.collection('NickNames of Users').document(userID).get().then((data){
setState(() {
users = data;
nick = users['MyNickName'];
print(nick);
});
});
}

Flutter: Change state depending on validation

I am building a simple forgot password form for a demo app which consists of one TextFormFields and a FloatingActionButton to submit the data. I have realised that the FloatingActionButton doesn't have disabled boolean state as such, so I wanted to try and replicate it by change the state to _isValid: true/ false depending on the TextFormField validation functions, which I can then put some ternary operators on FloatingActionButton to change the color and the functionality, depending on the state of this widget.
You will be able to see that I have the _autoValidate set to true on mounting of the widget then I try and trigger a UI reload in the _validateForgetEmail function. When I trigger these state changes I get a big UI error saying
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
flutter: The following assertion was thrown building Form-[LabeledGlobalKey<FormState>#0a40e](dirty, state:
flutter: FormState#59216):
flutter: setState() or markNeedsBuild() called during build.
flutter: This ForgotPasswordForm widget cannot be marked as needing to build because the framework is already
flutter: in the process of building widgets. A widget can be marked as needing to be built during the build
flutter: phase only if one of its ancestors is currently building. This exception is allowed because the
flutter: framework builds parent widgets before children, which means a dirty descendant will always be
flutter: built. Otherwise, the framework might not visit this widget during this build phase.
Code is below:
class ForgotPasswordForm extends StatefulWidget {
#override
_ForgotPasswordFormState createState() => _ForgotPasswordFormState();
}
Class _ForgotPasswordFormState extends State<ForgotPasswordForm> {
final _emailController = TextEditingController();
final _formKey = GlobalKey<FormState>();
final bool _autoValidate = true;
bool _isLoading = false;
bool _isValid = false;
String email;
#override
Widget build(BuildContext context) {
// Build a Form widget using the _formKey created above.
return Form(
key: _formKey,
child: _isLoading
? _buildLoadingSpinner(context)
: _buildPasswordForm(context),
autovalidate: _autoValidate,
);
}
Widget _buildLoadingSpinner(BuildContext context) {
return (Center(child: CircularProgressIndicator()));
}
Widget _buildPasswordForm(BuildContext context) {
print('isValid: ' + _isValid.toString());
return Column(
children: <Widget>[
Text(
'Please enter your email address.',
style: TextStyle(fontSize: 14.0),
textAlign: TextAlign.center,
),
Text(
'You will recieve a link to reset your password.',
style: TextStyle(fontSize: 14.0),
textAlign: TextAlign.center,
),
SizedBox(height: 32.0),
TextFormField(
controller: _emailController,
validator: _validateForgetEmail,
keyboardType: TextInputType.emailAddress,
autovalidate: _autoValidate,
style: TextStyle(fontSize: 14.0),
onSaved: (String val) {
email = val;
},
decoration: InputDecoration(
filled: true,
contentPadding: EdgeInsets.symmetric(horizontal: 15, vertical: 8),
labelText: 'Email',
border: InputBorder.none,
labelStyle: TextStyle(fontSize: 14.0, color: Colors.lightBlueAccent),
errorStyle: TextStyle(fontSize: 10.0, height: 0.5),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.lightGreenAccent, width: 2.0),
),
),
),
SizedBox(height: 24.0),
FloatingActionButton(
backgroundColor: _isValid ? Colors.lightBlue : Colors.grey,
onPressed: () {
_submitPasswordReset();
},
child: Icon(Icons.arrow_forward_ios, size: 14.0),
)
],
mainAxisAlignment: MainAxisAlignment.center,
);
}
void _submitPasswordReset() async {
if (_formKey.currentState.validate()) {
setState(() {
_isLoading = true;
});
UserPasswordResetRequest newPasswordRequest =
new UserPasswordResetRequest(email: _emailController.text);
http.Response response = await ApiService.queryPost(
'/api/users/password-forgot',
body: newPasswordRequest.toJson());
final int statusCode = response.statusCode;
if (statusCode == 400) {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('Wrong email or password'),
duration: Duration(seconds: 3),
backgroundColor: Colors.red));
setState(() {
_isLoading = false;
});
}
if (statusCode == 200) {
// setState(() {
// _isLoading = false;
// });
Navigator.push(
context,
MaterialPageRoute(builder: (context) => UserBackToLogin()),
);
}
setState(() {
_isLoading = false;
});
}
}
String _validateForgetEmail(String value) {
String patttern =
r"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))#((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$";
RegExp regExp = new RegExp(patttern);
if (value.length == 0) {
return "Email is Required";
} else if (!regExp.hasMatch(value)) {
setState(() {
_isValid = false;
});
return "Must be a valid email address";
}
print('value' + value);
setState(() {
_isValid = true;
});
return null;
}
}
Any insight would be great to see what I am doing wrong - very new to flutter. If you need any more info, then I can provide.
Cheers Sam
You can do it like this:
Split _validateForgetEmail method in two:
String _validateForgetEmail(String value) {
if (value.length == 0) {
return "Email is Required";
} else if (!_isEmailValid(value)) {
return "Must be a valid email address";
}
print('value' + value);
return null;
}
bool _isEmailValid(String value) {
String pattern =
r"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))#((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$";
RegExp regExp = new RegExp(pattern);
return regExp.hasMatch(value);
}
Now these methods only validate values without affecting any state.
Listen to _emailController changes
#override
void initState() {
super.initState();
_emailController.addListener(() {
final isEmailValid = _isEmailValid(_emailController.value.text);
if(isEmailValid != _isValid) {
setState(() {
_isValid = isEmailValid;
});
}
});
}
Also don't forget to dispose _emailController
#override
void dispose() {
_emailController.dispose();
super.dispose();
}
Exception explanation:
TextFormField extends FormField class. If autovalidate is turned on, then function passed as validator will be called in FormFieldState.build method to update error text.
So it leads to setState being called from build which is not allowed by framework
A simpler way to achieve this would be to validate in the onChanged callback.
class FormPage extends StatefulWidget {
#override
_FormPageState createState() => _FormPageState();
}
class _FormPageState extends State<FormPage> {
final _formKey = GlobalKey<FormState>();
bool _isValid = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Form(
key: _formKey,
onChanged: () {
final isValid = _formKey.currentState.validate();
if (_isValid != isValid) {
setState(() {
_isValid = isValid;
});
}
},
child: Column(
children: <Widget>[
TextFormField(validator: (x) => x.length > 2 ? null : 'Too short'),
],
),
),
floatingActionButton: Opacity(
opacity: _isValid ? 1 : 0.5,
child: FloatingActionButton(
child: Icon(Icons.send),
onPressed: () {},
),
),
);
}
}
Simple use
Form(
key: _key,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Column(
children: [
//All FormTextFields Here as you went....
],
),
),
Bit late to the party, but you can use setState directly inside the validator by adding a post frame callback in the validator as follows:
validator: (val) {
if (val == '') {
SchedulerBinding.instance.addPostFrameCallback((duration) {
setState(() {
someErrorVariableYouWantToChange = true;
});
});
} else {
SchedulerBinding.instance.addPostFrameCallback((duration) {
setState(() {
someErrorVariableYouWantToChange = false;
});
});
}
return null;
}
This will ensure that setState is called after the build process is complete due to the validator function.

Getting an error using sqflite plugin in my CRUD Flutter app

I'm super noob in Flutter and I'm trying a "trivial" (for everyone else)
CRUD app so I followed some tutorial
(https://grokonez.com/android/flutter-sqlite-example-listview-crud-operations-sqflite-plugin)
his "version" obviously works, of course mine doesn't.
I got this error: https://i.stack.imgur.com/TM893.png
here's "my" code, i guess the parts involved are DBHelper and TicketScreen:
class DatabaseHelper {
static final DatabaseHelper _instance = new DatabaseHelper.internal();
factory DatabaseHelper() => _instance;
final String ticketTable = 'ticketTable';
final String columnId = 'id';
final String columnDay = 'day';
final String columnMonth = 'month';
final String columnYear = 'year';
final String columnFrom = 'from';
final String columnTo = 'to';
final String columnAircraft = 'aircraft';
final String columnAirline = 'airline';
static Database _db;
DatabaseHelper.internal();
Future<Database> get db async {
if (_db != null) {
return _db;
}
_db = await initDb();
return _db;
}
initDb() async {
String databasesPath = await getDatabasesPath();
String path = join(databasesPath, 'ticket.db');
var db = await openDatabase(path, version: 1, onCreate: _onCreate);
return db;
}
void _onCreate(Database db, int newVersion) async {
await db.execute(
'CREATE TABLE $ticketTable($columnId INTEGER PRIMARY KEY, $columnDay TEXT, $columnMonth TEXT, $columnYear TEXT, $columnFrom TEXT, $columnTo TEXT, $columnAircraft TEXT, $columnAirline TEXT)');
}
Future<int> saveTicket(Ticket ticket) async {
var dbClient = await db;
var result = await dbClient.insert(ticketTable, ticket.toMap());
return result;
}
Future<List> getAllTickets() async {
var dbClient = await db;
var result = await dbClient.query(ticketTable, columns: [
columnId,
columnDay,
columnMonth,
columnYear,
columnFrom,
columnTo,
columnAircraft,
columnAirline
]);
return result.toList();
}
Future<int> getCount() async {
var dbClient = await db;
return Sqflite.firstIntValue(
await dbClient.rawQuery('SELECT COUNT(*) FROM $ticketTable'));
}
Future<Ticket> getTicket(int id) async {
var dbClient = await db;
List<Map> result = await dbClient.query(ticketTable,
columns: [
columnId,
columnDay,
columnMonth,
columnYear,
columnFrom,
columnTo,
columnAircraft,
columnAirline
],
where: '$columnId = ?',
whereArgs: [id]);
if (result.length > 0) {
return new Ticket.fromMap(result.first);
}
return null;
}
Future<int> deleteTicket(int id) async {
var dbClient = await db;
return await dbClient
.delete(ticketTable, where: '$columnId = ?', whereArgs: [id]);
}
Future<int> updateTicket(Ticket ticket) async {
var dbClient = await db;
return await dbClient.update(ticketTable, ticket.toMap(),
where: "$columnId = ?", whereArgs: [ticket.id]);
}
Future close() async {
var dbClient = await db;
return dbClient.close();
}
}
class TicketScreen extends StatefulWidget {
final Ticket ticket;
TicketScreen(this.ticket);
#override
State<StatefulWidget> createState() => new _TicketScreenState();
}
class _TicketScreenState extends State<TicketScreen> {
DatabaseHelper db = new DatabaseHelper();
TextEditingController _dayController;
TextEditingController _monthController;
TextEditingController _yearController;
TextEditingController _fromController;
TextEditingController _toController;
TextEditingController _aircraftController;
TextEditingController _airlineController;
#override
void initState() {
super.initState();
_dayController = new TextEditingController(text: widget.ticket.day);
_monthController = new TextEditingController(text: widget.ticket.month);
_yearController = new TextEditingController(text: widget.ticket.year);
_fromController = new TextEditingController(text: widget.ticket.from);
_toController = new TextEditingController(text: widget.ticket.to);
_aircraftController = new TextEditingController(text: widget.ticket.aircraft);
_airlineController = new TextEditingController(text: widget.ticket.airline);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Ticket')),
body: Container(
margin: EdgeInsets.all(15.0),
alignment: Alignment.center,
child: Column(
children: <Widget>[
Text('Travel Date',
style: new TextStyle(fontWeight: FontWeight.bold),
),
Row(
children: <Widget>[
Container(
alignment: Alignment.center,
width: 80.0,
child: TextField(
maxLength: 2,
keyboardType: TextInputType.number,
controller: _dayController,
decoration: InputDecoration(labelText: 'Day'),
),
),
Text(' / '),
Container(
width: 80.0,
child: TextField(
maxLength: 2,
keyboardType: TextInputType.number,
controller: _monthController,
decoration: InputDecoration(labelText: 'Month'),
),
),
Text(' / '),
Container(
width: 160.0,
child: TextField(
maxLength: 4,
keyboardType: TextInputType.number,
controller: _yearController,
decoration: InputDecoration(labelText: 'Year'),
),
),
],
),
Padding(padding: new EdgeInsets.all(5.0)),
Row(
children: <Widget>[
Container(
width: 160.0,
child: TextField(
controller: _fromController,
decoration: InputDecoration(labelText: 'From'),
),
),
Container(
width: 160.0,
child: TextField(
controller: _toController,
decoration: InputDecoration(labelText: 'To'),
),
),
],
),
Padding(padding: new EdgeInsets.all(5.0)),
Row(
children: <Widget>[
Container(
width: 160.0,
child: TextField(
controller: _aircraftController,
decoration: InputDecoration(labelText: 'Aircraft'),
),
),
Container(
width: 160.0,
child: TextField(
controller: _airlineController,
decoration: InputDecoration(labelText: 'Airline'),
),
),
],
),
Padding(padding: new EdgeInsets.all(5.0)),
RaisedButton(
child: (widget.ticket.id != null) ? Text('Update') : Text('Add'),
onPressed: () {
if (widget.ticket.id != null) {
db
.updateTicket(Ticket.fromMap({
'id': widget.ticket.id,
'day': _dayController.text,
'year': _yearController.text,
'from': _fromController.text,
'to': _toController.text,
'aircraft': _aircraftController.text,
'airline': _airlineController.text
}))
.then((_) {
Navigator.pop(context, 'update');
});
} else {
db
.saveTicket(Ticket(
_dayController.text,
_monthController.text,
_yearController.text,
_fromController.text,
_toController.text,
_aircraftController.text,
_airlineController.text))
.then((_) {
Navigator.pop(context, 'save');
});
}
},
),
],
),
),
);
}
}
and then some other stuff, but i tend to rule them out as the source of the problem
class MyListView extends StatefulWidget {
#override
_ListViewState createState() => new _ListViewState();
}
class _ListViewState extends State<MyListView> {
List<Ticket> items = new List();
DatabaseHelper db = new DatabaseHelper();
#override
void initState() {
super.initState();
db.getAllTickets().then((tickets) {
setState(() {
tickets.forEach((ticket) {
items.add(Ticket.fromMap(ticket));
});
});
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Ticket List',
home: Scaffold(
appBar: AppBar(
title: Text('Ticket List'),
centerTitle: true,
backgroundColor: Colors.blue,
),
body: Center(
child: ListView.builder(
itemCount: items.length,
padding: const EdgeInsets.all(15.0),
itemBuilder: (context, position) {
return Column(
children: <Widget>[
Divider(height: 5.0),
ListTile(
title: Text(
'From ${items[position].from} to ${items[position].to}',
style: TextStyle(
fontSize: 22.0,
color: Colors.deepOrangeAccent,
),
),
subtitle:
Text(
'Operated by ${items[position].airline} with ${items[position].aircraft} ',
style: new TextStyle(
fontSize: 18.0,
fontStyle: FontStyle.italic,
),
),
leading: Column(
children: <Widget>[
Padding(padding: EdgeInsets.all(10.0)),
CircleAvatar(
backgroundColor: Colors.blueAccent,
radius: 20.0,
child: Text(
'${items[position].day}/${items[position].month}/${items[position].year}',
style: TextStyle(
fontSize: 22.0,
color: Colors.white,
),
),
),
IconButton(
icon: const Icon(Icons.remove_circle_outline),
onPressed: () => _deleteTicket(context, items[position], position)
),
],
),
onTap: () => _navigateToTicket(context, items[position]),
),
],
);
}),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () => _createNewTicket(context),
),
),
);
}
void _deleteTicket(BuildContext context, Ticket ticket, int position) async {
db.deleteTicket(ticket.id).then((tickets) {
setState(() {
items.removeAt(position);
});
});
}
void _navigateToTicket(BuildContext context, Ticket ticket) async {
String result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => TicketScreen(ticket)),
);
if (result == 'update') {
db.getAllTickets().then((tickets) {
setState(() {
items.clear();
tickets.forEach((ticket) {
items.add(Ticket.fromMap(ticket));
});
});
});
}
}
void _createNewTicket(BuildContext context) async {
String result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => TicketScreen(Ticket('', '', '', '', '', '', ''))),
);
if (result == 'save') {
db.getAllTickets().then((tickets) {
setState(() {
items.clear();
tickets.forEach((ticket) {
items.add(Ticket.fromMap(ticket));
});
});
});
}
}
}
class Ticket {
int _id;
String _day;
String _month;
String _year;
String _from;
String _to;
String _aircraft;
String _airline;
Ticket(this._day, this._month, this._year, this._from, this._to, this._aircraft, this._airline);
Ticket.map(dynamic obj) {
this._id = obj['id'];
this._day = obj['day'];
this._month = obj['month'];
this._year = obj['year'];
this._from = obj['form'];
this._to = obj['to'];
this._aircraft = obj['aircraft'];
this._airline = obj['airline'];
}
int get id => _id;
String get day => _day;
String get month => _month;
String get year => _year;
String get from => _from;
String get to =>_to;
String get aircraft =>_aircraft;
String get airline => _airline;
Map<String, dynamic> toMap() {
var map = new Map<String, dynamic>();
if (_id != null) {
map['id'] = _id;
}
map['day'] = _day;
map['month'] = _month;
map['year'] = _year;
map['from'] = _from;
map['to'] = _to;
map['aircraft'] = _aircraft;
map['airline'] = _airline;
return map;
}
Ticket.fromMap(Map<String, dynamic> map) {
this._id = map['id'];
this._day = map['day'];
this._month = map['month'];
this._year = map['year'];
this._from = map['from'];
this._to = map['to'];
this._aircraft = map['aircraft'];
this._airline = map['airline'];
}
}
void main() => runApp(
MaterialApp(
title: 'Returning Data',
home: MyListView(),
),
);
so, can you tell me what I did wrong?
thank you in advance
bonus question:
I feel no love for SQL,
is there any alternative db for flutter?
I mean real DB, not saving data in sharedpref or using firebase,
I like realm, but looks like there's not a flutter.
thanks again
The problem is you are using a reserved keyword 'From' in your column name, you also use 'To'
'CREATE TABLE $ticketTable($columnId INTEGER PRIMARY KEY, $columnDay TEXT, $columnMonth TEXT, $columnYear TEXT, $columnFrom TEXT, $columnTo TEXT, $columnAircraft TEXT, $columnAirline TEXT)');
So essentially you are asking it to, Create a table called TicketTable with the following column names -id,day,month,year.. that hold these specific data types. But when it gets to the column called 'from' it thinks you want to create a null (since column name doesn't exist) FROM the table called 'Text', which makes no sense.
Here is a list of keywords you should avoid - https://docs.oracle.com/database/121/SQLRF/ap_keywd001.htm#SQLRF55621
You might be able to get around this by surrounding the variable with double quotes but you'll have to test that out for yourself.

Resources