Flutter - Dynamic list with data coming from the database - database

I needed to create the DialogItem widgets using the data that comes from the database. I tried to use for(){} but it did not work.
Could you help me solve this problem?
I put the used code as well as the evidence that works, just does not work the dynamic list of DialogItem with the data of the database.
To work the code below, you need to insert the sqflite and path_provider dependencies into pubspec.yaml, thus:
dependencies:
sqflite: any
path_provider: any
flutter:
sdk: flutter
The DatabaseClient class will create the database with 3 records.
In the gif only foo1 appears the correct one would be to appear all the values of the list that came from the database:
[{name: foo1, color: 0}, {name: foo2, color: 1}, {name: foo3, color: 2}]
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
DatabaseClient _db = new DatabaseClient();
int number;
List listCategory;
List colors = [
const Color(0xFFFFA500),
const Color(0xFF279605),
const Color(0xFF005959)
];
createdb() async {
await _db.create().then(
(data){
_db.countCategory().then((list){
this.number = list[0][0]['COUNT(*)']; //3
this.listCategory = list[1];
//[{name: foo1, color: 0}, {name: foo2, color: 1}, {name: foo3, color: 2}]
});
}
);
}
#override
void initState() {
super.initState();
createdb();
}
void showCategoryDialog<T>({ BuildContext context, Widget child }) {
showDialog<T>(
context: context,
child: child,
)
.then<Null>((T value) {
if (value != null) {
setState(() { print(value); });
}
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(),
body: new Center(
child: new RaisedButton(
onPressed: (){
showCategoryDialog<String>(
context: context,
child: new SimpleDialog(
title: const Text('Categories'),
children: <Widget>[
//for(var i = 0; i < this.number; i++) {
new DialogItem(
icon: Icons.brightness_1,
color: this.colors[
this.listCategory[0]['color']
//the zero should be dynamic going from 0 to 2 with the for(){}
//but o for(){} dont work
],
text: this.listCategory[0]['name'],
onPressed: () {
Navigator.pop(context, this.listCategory[0]['name']);
}
),
//}
]
)
);
},
child: new Text("ListButton"),
)
),
);
}
}
//Creating Database with some data and two queries
class DatabaseClient {
Database db;
Future create() async {
Directory path = await getApplicationDocumentsDirectory();
String dbPath = join(path.path, "database.db");
db = await openDatabase(dbPath, version: 1, onCreate: this._create);
}
Future _create(Database db, int version) async {
await db.execute("""
CREATE TABLE category (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
color INTEGER NOT NULL
)""");
await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo1', 0)");
await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo2', 1)");
await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo3', 2)");
}
Future countCategory() async {
Directory path = await getApplicationDocumentsDirectory();
String dbPath = join(path.path, "database.db");
Database db = await openDatabase(dbPath);
var count = await db.rawQuery("SELECT COUNT(*) FROM category");
List list = await db.rawQuery('SELECT name, color FROM category');
await db.close();
return [count, list];
}
}
//Class of Dialog Item
class DialogItem extends StatelessWidget {
DialogItem({
Key key,
this.icon,
this.size,
this.color,
this.text,
this.onPressed }) : super(key: key);
final IconData icon;
double size = 36.0;
final Color color;
final String text;
final VoidCallback onPressed;
#override
Widget build(BuildContext context) {
return new SimpleDialogOption(
onPressed: onPressed,
child: new Container(
child: new Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Container(
child: new Container(
margin: size == 16.0 ? new EdgeInsets.only(left: 7.0) : null,
child: new Icon(icon, size: size, color: color),
)
),
new Padding(
padding: size == 16.0 ?
const EdgeInsets.only(left: 17.0) :
const EdgeInsets.only(left: 16.0),
child: new Text(text),
),
],
),
)
);
}
}

There might be other issues, but as a start, I think this code
for(var i = 0; i < this.number; i++) {
...
}
should be changed to
children: this.number == null ? null :
new List(this.number).map((i) =>
new DialogItem(
icon: Icons.brightness_1,
color: this.colors[
this.listCategory[0]['color']
//the zero should be dynamic going from 0 to 2 with the for(){}
//but o for(){} dont work
],
text: this.listCategory[0]['name'],
onPressed: () {
Navigator.pop(context, this.listCategory[0]['name']);
}
).toList(),
to not throw an exception when this.number is null (response not yet received from the database).
and wrap the code that updates the state with setState(() {...})
createdb() async {
await _db.create().then(
(data){
_db.countCategory().then((list){
setState(() {
this.number = list[0][0]['COUNT(*)']; //3
this.listCategory = list[1];
//[{name: foo1, color: 0}, {name: foo2, color: 1}, {name: foo3, color: 2}]
});
});
}
);
}

I found the solution, according to the Flutter - Build Widgets dynamically and Flutter - Combine dynamically generated elements with hard-coded ones and in this question
As SimpleDialog only accepts a List of type Widget - <Widget>[] I declare a variable tiles of type List<Widget> - List<Widget> tiles; and created a function of type List<Widget> - List<Widget> buildTile(int counter) {... - to be able to return a List<Widget>
Because of Navigator.pop (context, ... I needed to create the buildTile() function inside the Widget build(BuildContext context) {...
In the buildTile() function I added a for() to insert into the Widget type list as many DialogItem Widgets were needed, according to the result that comes from the database
and wrap the code that updates the state with setState(() {...}) as explained by Günter Zöchbauer
setState(() {
this.number = list[0][0]['COUNT(*)']; //3
this.listCategory = list[1];
//[{name: foo1, color: 0}, {name: foo2, color: 1}, {name: foo3, color: 2}]
})
The complete code working as well as the demo are below:
To work the code below, you need to insert the sqflite and path_provider dependencies into pubspec.yaml, thus:
dependencies:
sqflite: any
path_provider: any
flutter:
sdk: flutter
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
DatabaseClient _db = new DatabaseClient();
int number;
List listCategory;
List<Widget> tiles;
List colors = [
const Color(0xFFFFA500),
const Color(0xFF279605),
const Color(0xFF005959)
];
createdb() async {
await _db.create().then(
(data){
_db.countCategory().then((list){
setState(() {
this.number = list[0][0]['COUNT(*)']; //3
this.listCategory = list[1];
//[{name: foo1, color: 0}, {name: foo2, color: 1}, {name: foo3, color: 2}]
});
});
}
);
}
#override
void initState() {
super.initState();
createdb();
}
void showCategoryDialog<T>({ BuildContext context, Widget child }) {
showDialog<T>(
context: context,
child: child,
)
.then<Null>((T value) {
if (value != null) {
setState(() { print(value); });
}
});
}
#override
Widget build(BuildContext context) {
List<Widget> buildTile(int counter) {
this.tiles = [];
for(var i = 0; i < counter; i++) {
this.tiles.add(
new DialogItem(
icon: Icons.brightness_1,
color: this.colors[
this.listCategory[i]['color']
],
text: this.listCategory[i]['name'],
onPressed: () {
Navigator.pop(context, this.listCategory[i]['name']);
}
)
);
}
return this.tiles;
}
return new Scaffold(
appBar: new AppBar(),
body: new Center(
child: new RaisedButton(
onPressed: (){
showCategoryDialog<String>(
context: context,
child: new SimpleDialog(
title: const Text('Categories'),
children: buildTile(this.number)
)
);
},
child: new Text("ListButton"),
)
),
);
}
}
//Creating Database with some data and two queries
class DatabaseClient {
Database db;
Future create() async {
Directory path = await getApplicationDocumentsDirectory();
String dbPath = join(path.path, "database.db");
db = await openDatabase(dbPath, version: 1, onCreate: this._create);
}
Future _create(Database db, int version) async {
await db.execute("""
CREATE TABLE category (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
color INTEGER NOT NULL
)""");
await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo1', 0)");
await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo2', 1)");
await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo3', 2)");
}
Future countCategory() async {
Directory path = await getApplicationDocumentsDirectory();
String dbPath = join(path.path, "database.db");
Database db = await openDatabase(dbPath);
var count = await db.rawQuery("SELECT COUNT(*) FROM category");
List list = await db.rawQuery('SELECT name, color FROM category');
await db.close();
return [count, list];
}
}
//Class of Dialog Item
class DialogItem extends StatelessWidget {
DialogItem({
Key key,
this.icon,
this.size,
this.color,
this.text,
this.onPressed }) : super(key: key);
final IconData icon;
double size = 36.0;
final Color color;
final String text;
final VoidCallback onPressed;
#override
Widget build(BuildContext context) {
return new SimpleDialogOption(
onPressed: onPressed,
child: new Container(
child: new Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Container(
child: new Container(
margin: size == 16.0 ? new EdgeInsets.only(left: 7.0) : null,
child: new Icon(icon, size: size, color: color),
)
),
new Padding(
padding: size == 16.0 ?
const EdgeInsets.only(left: 17.0) :
const EdgeInsets.only(left: 16.0),
child: new Text(text),
),
],
),
)
);
}
}

Related

How to create a form using dynamic fields from a JSON array in Flutter

I want to create a form with dynamic fields. The fields are fetched from a database; these are determined by a user on the web application and then displayed to another user on the mobile application so the fields keep changing.
Although there are no errors in the code, the form does not display. I have simplified it to a single input field so as to test the logic. Below is what I have tried so far:
the file to create the form :
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:mne/Network/api.dart';
import 'form_model.dart';
class CreateForm extends StatefulWidget {
const CreateForm({Key? key}) : super(key: key);
#override
State<CreateForm> createState() => _CreateFormState();
}
class _CreateFormState extends State<CreateForm> {
late List<Map<String, dynamic>> _values;
final List<FormFields> _formFields = <FormFields>[];
#override
void initState() {
super.initState();
_values = [];
}
Future<List<FormFields>> _fetchFormFields() async {
var res = await Network().getData('company/fields');
var fields = <FormFields>[];
if (res.StatusCode == 200) {
var body = json.decode(res.body);
var tdata = body['data'];
var formsJson = tdata;
for (var formJson in formsJson) {
fields.add(FormFields.fromJson(formJson));
}
}
return fields;
}
_displayForm(index) {
_fetchFormFields().then((value) {
_formFields.addAll(value);
});
if (_formFields[index].type == "text") {
return Padding(
padding: const EdgeInsets.all(8),
child: TextFormField(
decoration: InputDecoration(
labelText: _formFields[index].name,
enabledBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.green),
borderRadius: BorderRadius.circular(10)),
),
onChanged: (val) {},
));
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(10),
child: Column(
children: [
Flexible(
child: ListView.builder(
shrinkWrap: true,
itemCount: _formFields.length,
itemBuilder: (context, index) {
return _displayForm(index);
}))
],
)));
}
}
the form model:
class FormFields {
String name;
String type;
String option;
FormFields({required this.name, required this.type, required this.option});
factory FormFields.fromJson(Map<String, dynamic> json) => FormFields(
name: json['name'],
type: json['type'],
option: json['option'],
);
Map<String, String> toJson() =>
{'name': name, 'type': type, 'option': option};
}
I want a circular progress indicator to show loading. I also want the user to move from the login directly to a display of the form so I put the create form class inside another one, to make it easier to reference in the login button. I called it test :
import 'package:flutter/material.dart';
import 'package:mne/Forms/create_form.dart';
class Test extends StatefulWidget {
const Test({Key? key}) : super(key: key);
#override
State<Test> createState() => _TestState();
}
class _TestState extends State<Test> {
bool _isLoading = true;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(children: [
if (_isLoading)
Container(
alignment: Alignment.center,
child: ElevatedButton(
child: const Text('Add Form'),
onPressed: () async {
setState(() {
_isLoading = false;
});
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const CreateForm()));;
if (!mounted) return;
setState(() {
_isLoading = true;
});
},
))
else
const Center(
child: CircularProgressIndicator(backgroundColor: Color.fromRGBO(0, 161, 39, 1)),
)
]));
}
}
Currently, after clicking on the login button which navigates to the test page it displays the add form button.
On clicking the add form button, the circular progress indicator displays very briefly before displaying a blank page. Should I perhaps add all the possible field type options for it to work? Please let me know where I have gone wrong thanks

Same date is displaying for all the notes in flutter. Date is not updating dynamically

I am a beginner in flutter and I am building a simple note-taking app. Whenever a user creates a note, the date month, and year of creation will get inserted into SQflite DB and I am fetching the same on the main screen. But for all other notes also same date month and year are displaying. I don't know where it went wrong, any help here, please. I am stuck in this for the past 2 days. I have checked other answers in StackOverflow, from there I learnt the implementation of date, but couldn't resolve this problem.
Database Helper Class
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'model_notes.dart';
class DatabaseHelper {
static final _databaseName = "myNote.db";
static final _databaseVersion = 1;
static final table = 'notes_table';
static final columnId = 'id';
static final columnTitle = 'title';
static final columnBody = 'body';
static final columnDate = 'date';
DatabaseHelper._privateConstructor();
static final DatabaseHelper instance = DatabaseHelper._privateConstructor();
static Database _database;
Future<Database> get database async {
if (_database != null) return _database;
_database = await initDatabase();
return _database;
}
initDatabase() async {
String path = join(await getDatabasesPath(), _databaseName);
return await openDatabase(path,
version: _databaseVersion, onCreate: _onCreate);
}
Future _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE $table (
$columnId INTEGER PRIMARY KEY AUTOINCREMENT,
$columnTitle TEXT NOT NULL,
$columnBody TEXT NOT NULL,
$columnDate TEXT NOT NULL
)
''');
}
Future<int> insert(Note note) async {
Database db = await instance.database;
if (note.title.trim().isEmpty) note.title = 'Untitled Note';
return await db.insert(table, {'title': note.title, 'body': note.body, 'date': note.date});
}
Future<List<Note>> getNotesFromDB() async {
final db = await database;
List<Note> notesList = [];
List<Map> maps = await db.query(table);
if (maps.length > 0) {
maps.forEach((map) {
notesList.add(Note.fromMap(map));
});
}
return notesList;
}
}
This is my note model class
import 'db_operations.dart';
class Note {
int id;
String title;
String body;
String date;
Note(this.id, this.title, this.body, this.date);
Note.fromMap(Map<String, dynamic> map) {
id = map['id'];
title = map['title'];
body = map['body'];
date = map['date'];
}
Map<String, dynamic> toMap() {
return {
DatabaseHelper.columnId: id,
DatabaseHelper.columnTitle: title,
DatabaseHelper.columnBody: body,
DatabaseHelper.columnDate: date,
};
}
#override
String toString() {
return '$title, $date';
}
}
This is where I am adding Notes
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:note_taking_app/constants/buttons_and_icons_misc(classes).dart';
import 'package:note_taking_app/db/db_operations.dart';
import 'package:note_taking_app/db/model_notes.dart';
final bodyController = TextEditingController();
final headerController = TextEditingController();
final dbHelper = DatabaseHelper.instance;
String formattedDate = DateFormat.yMMMd('en_US').format(DateTime.now());
class AddingNotes extends StatefulWidget {
#override
_AddingNotesState createState() => _AddingNotesState();
}
class _AddingNotesState extends State<AddingNotes> {
#override
void initState() {
super.initState();
bodyController.clear();
headerController.clear();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
backwardsCompatibility: true,
leading: LeadingIcon(
callBack: () {
Navigator.pop(context);
},
),
backgroundColor: Colors.white.withOpacity(0.4),
actions: <Widget>[
ActionsIconButton(
icon: Icon(undo, color: black),
callBack: () {
debugPrint('undo tapped');
},
),
ActionsIconButton(
icon: Icon(redo, color: black),
callBack: () {
debugPrint('redo tapped');
},
),
ActionsIconButton(
icon: Icon(save, color: black),
callBack: () async {
String title = headerController.text;
String body = bodyController.text;
Note note = Note(20, title, body, formattedDate);
var value = await dbHelper.insert(note);
Navigator.pop(context);
},
)
],
),
body: Container(
color: Colors.white.withOpacity(0.4),
child: Padding(
padding: const EdgeInsets.all(13.0),
child: Column(
children: [
HeaderBody(
textEditingController: headerController,
),
SizedBox(
height: 32.0,
),
Expanded(
child: NotesBody(
textEditingController: bodyController,
),
),
],
),
),
),
);
}
}
This is where I am displaying notes
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:note_taking_app/constants/buttons_and_icons_misc(classes).dart';
import 'package:note_taking_app/constants/text_and_decorations(methods).dart';
import 'package:note_taking_app/db/model_notes.dart';
import 'package:note_taking_app/ui/adding_notes.dart';
class MainScreen extends StatefulWidget {
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
List<Note> noteList = [];
#override
void initState() {
super.initState();
dbHelper.initDatabase();
setNotesFromDB();
}
setNotesFromDB() async {
print("Entered setNotes in main page");
var fetchedNotes = await dbHelper.getNotesFromDB();
setState(() {
noteList = fetchedNotes;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: mainScreenAppBar,
floatingActionButton: FAB(
whatToDo: () {
gotoAddingNotesPage(context);
},
),
body: SafeArea(
child: ListView.builder(
itemCount: noteList.length,
itemBuilder: (context, index) {
return TileCard(
titleText: ('${noteList[index].title}'),
dateText: ('${noteList[index].date}'),
);
},
),
),
);
}
gotoAddingNotesPage(BuildContext context) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => AddingNotes()),
).then((value) => setState(() {
setNotesFromDB();
}));
}
}
This is my TileCard class where I've declared dateText widget
class TileCard extends StatelessWidget {
final String titleText;
final String dateText;
const TileCard({#required this.titleText, #required this.dateText});
#override
Widget build(BuildContext context) {
return Container(
height: 90,
child: Card(
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
elevation: 0,
margin: const EdgeInsets.all(8.0),
child: ListTile(
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
title: Text(
titleText,
style: TextStyle(fontWeight: FontWeight.w700),
),
tileColor: Colors.grey.withOpacity(0.2),
subtitle: Padding(
padding: EdgeInsets.only(top: 10.0),
child: Text(dateText),
),
),
),
);
}
}
I have added image, this is how it looks now.

I am creating note app in flutter and stuck in sqflite, how can I pass my title and body to insert method

I am building a simple note app, but I am stuck at saving the data (title and body) using SQLite.
DataBaseHelper class
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'model_notes.dart';
class DatabaseHelper {
static final _databaseName = "myNote.db";
static final _databaseVersion = 1;
static final table = 'notes_table';
static final columnId = 'id';
static final columnTitle = 'title';
static final columnBody = 'body';
DatabaseHelper._privateConstructor();
static final DatabaseHelper instance = DatabaseHelper._privateConstructor();
static Database _database;
Future<Database> get database async {
if (_database != null) return _database;
_database = await _initDatabase();
return _database;
}
_initDatabase() async {
String path = join(await getDatabasesPath(), _databaseName);
return await openDatabase(path,
version: _databaseVersion,
onCreate: _onCreate);
}
Future _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE $table (
$columnId INTEGER PRIMARY KEY AUTOINCREMENT,
$columnTitle TEXT NOT NULL,
$columnBody TEXT NOT NULL
)
''');
}
Future<int> insert(Note note) async {
Database db = await instance.database;
return await db.insert(table, {'title': note.title, 'body': note.body});
}
This is the Model Class for Notes
import 'db_operations.dart';
class Note {
int id;
String title;
String body;
Note(this.id, this.title, this.body);
Note.fromMap(Map<String, dynamic> map) {
id = map['id'];
title = map['title'];
body = map['body'];
}
Map<String, dynamic> toMap(){
return {
DatabaseHelper.columnId : id,
DatabaseHelper.columnTitle : title,
DatabaseHelper.columnBody : body
};
}
}
and this is where I'm calling insert method (class name = adding_notes.dart)
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:note_taking_app/constants/buttons_and_icons_misc(classes).dart';
import 'package:note_taking_app/db/db_operations.dart';
import 'package:note_taking_app/db/model_notes.dart';
import 'package:sqflite/sqflite.dart';
import 'main_screen.dart';
final bodyController = TextEditingController();
final headerController = TextEditingController();
final dbHelper = DatabaseHelper.instance;
class AddingNotes extends StatefulWidget {
#override
_AddingNotesState createState() => _AddingNotesState();
}
class _AddingNotesState extends State<AddingNotes> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
backwardsCompatibility: true,
leading: LeadingIcon(
callBack: () {
Navigator.pop(context);
},
),
backgroundColor: Colors.white.withOpacity(0.4),
actions: <Widget>[
ActionsIconButton(
icon: Icon(undo, color: black),
callBack: () {
debugPrint('undo tapped');
},
),
ActionsIconButton(
icon: Icon(redo, color: black),
callBack: () {
debugPrint('redo tapped');
},
),
ActionsIconButton(
icon: Icon(save, color: black),
callBack: () async {
debugPrint(bodyController.text);
debugPrint(headerController.text);
getHeaderDataToMainScreen(context);
String title = headerController.text;
String body = bodyController.text;
/*This is where I am calling insert method*/
dbHelper.insert(title, body);
},
)
],
),
body: Container(
color: Colors.white.withOpacity(0.4),
child: Padding(
padding: const EdgeInsets.all(13.0),
child: Column(
children: [
HeaderBody(
textEditingController: headerController,
),
SizedBox(
height: 32.0,
),
Expanded(
child: NotesBody(
textEditingController: bodyController,
),
),
],
),
),
),
);
}
}
getHeaderDataToMainScreen(BuildContext context){
Navigator.push(context,
MaterialPageRoute(
builder: (context) => MainScreen(
heading : headerController.text,
)
)
);
}
It is showing too many positional arguments expected 1 found 2. I know I need to send 1 argument, but how can I send both title and body as 1 argument. Maybe through List I can send it but I don't know how to do that. Any help here guys, I'm stuck at this for the past 5 days.
Check the example below that i have created based on the code you provided.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'db_operations.dart';
void main() {
runApp(MaterialApp(
debugShowCheckedModeBanner: false,
home: AddingNotes(),
));
}
final bodyController = TextEditingController();
final headerController = TextEditingController();
final dbHelper = DatabaseHelper.instance;
class AddingNotes extends StatefulWidget {
#override
_AddingNotesState createState() => _AddingNotesState();
}
class _AddingNotesState extends State<AddingNotes> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
leading: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
Icons.add,
color: Colors.black,
),
),
),
backgroundColor: Colors.white.withOpacity(0.4),
actions: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: GestureDetector(
onTap: () {
debugPrint('undo tapped');
},
child: Icon(
Icons.undo,
color: Colors.black,
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: GestureDetector(
onTap: () {
debugPrint('redo tapped');
},
child: Icon(
Icons.redo,
color: Colors.black,
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: GestureDetector(
onTap: () async {
debugPrint(bodyController.text);
debugPrint(headerController.text);
//getHeaderDataToMainScreen(context);
String title = headerController.text;
String body = bodyController.text;
Note note = Note(20, title, body);
var value = await dbHelper.insert(note);
print("if 1 is return then insert success and 0 then not inserted : $value");
},
child: Icon(
Icons.save,
color: Colors.black,
),
),
)
],
),
body: Container(
color: Colors.white.withOpacity(0.4),
child: Padding(
padding: const EdgeInsets.all(13.0),
child: Column(
children: [
TextField(
controller: headerController,
),
SizedBox(
height: 32.0,
),
Expanded(
child: TextField(
controller: bodyController,
),
),
],
),
),
),
);
}
}
// getHeaderDataToMainScreen(BuildContext context) {
// Navigator.push(context,
// MaterialPageRoute(
// builder: (context) =>
// MainScreen(
// heading: headerController.text,
// )
// )
// );
// }
class Note {
int id;
String title;
String body;
Note(this.id, this.title, this.body);
Note.fromMap(Map<String, dynamic> map) {
id = map['id'];
title = map['title'];
body = map['body'];
}
Map<String, dynamic> toMap() {
return {
DatabaseHelper.columnId: id,
DatabaseHelper.columnTitle: title,
DatabaseHelper.columnBody: body
};
}
}
This is just a sample demo example added every thing in one file and made many changes. change it as per your needs.
You don't need a auto increment id as you have given it in the database. Run the app and let me know if it works.

Add data instead of overwriting it

When I use pop up AddPlanScreen to add a note to the widget, it overwrites the document instead of adding it. Registering, logging and setting data work properly.
What I've tried to do:
Using FirebaseFirestore.instance.runTransaction but I couldn't use + operator for String as I add data to map.
set(..., SetOptions(merge:true))
update method
Do I have to try to create a new Map and add data there? I am new to programming and will be very grateful for any suggestions.
This is a method I use to set and fetch data
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class MyProvider extends ChangeNotifier {
Map<String, dynamic> _names = {};
String name(String key) => _names[key];
void setName(String key, String newString) {
_names[key] = newString;
var firebaseUser = FirebaseAuth.instance.currentUser;
FirebaseFirestore.instance
.collection('Notes')
.doc(firebaseUser.uid)
.set(_names);
}
void fetchData() {
var firebaseUser = FirebaseAuth.instance.currentUser;
FirebaseFirestore.instance
.collection('Notes')
.doc(firebaseUser.uid)
.get()
.then((DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
var data = documentSnapshot.data();
_names = data;
} else {
print('The document does not exist on the database');
}
});
}
}
This is Planner Screen where I show all the notes
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:my_planner_app/widgets/my_provider.dart';
import 'file:///C:/Users/krisk/AndroidStudioProjects/planner_app/lib/widgets/weekday_card.dart';
import 'package:provider/provider.dart';
class PlannerScreen extends StatefulWidget {
static const String id = 'planner_screen';
#override
_PlannerScreenState createState() => _PlannerScreenState();
}
class _PlannerScreenState extends State<PlannerScreen> {
Widget build(BuildContext context) {
Provider.of<MyProvider>(context, listen: false)
.fetchData();
var size = MediaQuery.of(context).size;
final double itemHeight = (size.height - 24) / 2;
final double itemWidth = size.width / 2;
return Scaffold(
backgroundColor: Color(0xFFcf9e9f),
body: Container(
child: GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: (itemWidth / itemHeight),
),
children: <Widget>[
WeekDayCard(
text: '',
),
WeekDayCard(text: 'Monday' ),
WeekDayCard(text: 'Tuesday'),
WeekDayCard(text: 'Wednesday'),
WeekDayCard(text: 'Thursday'),
WeekDayCard(text: 'Friday'),
WeekDayCard(text: 'Saturday'),
WeekDayCard(text: 'Sunday'),
WeekDayCard(text: 'Notes'),
],
),
),
);
}
}
This is associated WeekDayCard widget
import 'package:flutter/material.dart';
import 'package:my_planner_app/screens/addPlan_screen.dart';
import 'package:provider/provider.dart';
import 'package:my_planner_app/widgets/my_provider.dart';
class WeekDayCard extends StatefulWidget {
WeekDayCard({#required this.text, this.name});
final String name;
final String text;
#override
_WeekDayCardState createState() => _WeekDayCardState();
}
class _WeekDayCardState extends State<WeekDayCard> {
#override
Widget build(BuildContext context) {
return Consumer<MyProvider>(builder: (context, myProvider, child) {
return Card(
color: Color(0xFFFEEFCD),
elevation: 10,
child: Column(
children: [
Text(widget.text),
Text(Provider.of<MyProvider>(context).name(widget.text) ?? ''
),
Expanded(
child: InkWell(
onTap: () {
showModalBottomSheet(
backgroundColor: Color(0xFFFEEFCD),
context: context,
builder: (context) => AddPlanScreen(weekdayName: widget.text),
);
},
),
),
],
),
);
});
}
}
This is associated AddPlanScreen
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:my_planner_app/widgets/my_provider.dart';
class AddPlanScreen extends StatefulWidget {
final String weekdayName;
const AddPlanScreen({Key key, this.weekdayName}) : super(key: key);
#override
_AddPlanScreenState createState() => _AddPlanScreenState();
}
class _AddPlanScreenState extends State<AddPlanScreen> {
String name;
#override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: TextFormField(
onChanged: (text) {
name = text;
},
decoration: InputDecoration(
border: InputBorder.none,
),
minLines: 10,
maxLines: 30,
autocorrect: false,
),
),
FlatButton(
onPressed: () {
Provider.of<MyProvider>(context, listen: false)
.setName(widget.weekdayName, name);
Navigator.pop(context);
},
color: Colors.blue,
),
],
);
}
}
You should use set only when you are creating a document for the first time and you want to give it a designated ID (not randomly generated by firebase). Or, a second use is when you want to deliberately write over your existing data.
When you want to update a document, or a single value\entry in it, you just use: collection('yourCollection').doc('yourDocID').update({"nameOfnewField": "new data"})
This update method will not overwrite your existing document,it will only add a new field called "nameOfnewField", or if that field already exists, it will write over it only.
i.e if nameOfnewField was a value that was false, when you update it, with .update({"nameOfnewField": "true"}), it becomes true but the rest of the document is not changed.

How do I access separate children from Firebase database in Flutter?

Hi I am currently trying to access a child of my database in Flutter so that I can link it to another child. I am able to pull the data from one child, but unable to link this data with my other child. I want to be able to access a field of the 'Record' child and link to the 'Volunteer' child using the "volunteer" field but am unable to get them to link together. Any help would be appreciated!
The database structure is as follows:
This is the code for My Volunteer class:
class Volunteer {
String volunteerID;
String name;
String role;
Volunteer(this.volunteerID, this.name, this.role);
Volunteer.fromSnapshot(DataSnapshot snapshot)
: volunteerID = snapshot.key,
name = snapshot.value["name"],
role = snapshot.value["role"];
toJson() {
return {
"key": volunteerID,
"name": name,
"role": role
};
}
}
And this is the code for my UI:
class SignInPageState extends State<SignInPage> {
List<Volunteer> volunteers;
Volunteer volunteer;
DatabaseReference dbRef;
DatabaseReference volunteerRef;
DatabaseReference recordRef;
#override
void initState() {
super.initState();
volunteers = new List();
volunteer = Volunteer("","", "");
final FirebaseDatabase database = FirebaseDatabase.instance;
dbRef = database.reference();
volunteerRef = database.reference().child('volunteer');
recordRef = database.reference().child('record');
dbRef.onChildAdded.listen(_onEntryAdded);
dbRef.onChildChanged.listen(_onEntryChanged);
volunteerRef.once().then((DataSnapshot snapshot) {
Map<dynamic, dynamic> getMap = snapshot.value;
getMap.forEach((key, values) {
String volunteerRecord = recordRef.child('volunteer').toString();
if (volunteerRecord == volunteer.volunteerID){
volunteer.role = volunteerRecord;
}
volunteers.add(volunteer);
});
});
}
_onEntryAdded(Event event) {
setState(() {
volunteers.add(Volunteer.fromSnapshot(event.snapshot));
});
}
_onEntryChanged(Event event) {
var old = volunteers.singleWhere((entry) {
return entry.volunteerID == event.snapshot.key;
});
setState(() {
volunteers[volunteers.indexOf(old)] = Volunteer.fromSnapshot(event.snapshot);
});
}
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
padding: EdgeInsets.all(10.0),
color: Colors.white,
child: Column(
children: <Widget>[
Expanded(
flex: 8,
child: Container(
child: FirebaseAnimatedList(
query: volunteerRef,
itemBuilder: (BuildContext context, DataSnapshot snapshot,
Animation<double> animation, int index) {
return new ListTile(
title: Text(
(volunteers[index].name + " " + volunteers[index].role),
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.bold ,color: Color.fromRGBO(139, 195, 68, 1)),
),
onTap: () {
SuccessSignInPage(volunteers[index].name);
},
);
},
),
)
),
],
)),
),
);
}
void Back() async {
await Navigator.of(context).pop();
}
void SuccessSignInPage(String name) async {
var route = new MaterialPageRoute(
builder: (BuildContext context) => new SignInSuccessPage(value: name));
await Navigator.of(context).push(route);
}
}

Resources