【FLUTTER ANDROID STUDIO and IOS】Appointment System with SQLite
Posted: 28 Mar 2021 05:37 PM PDT
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "package:flutter/material.dart";
import "package:scoped_model/scoped_model.dart";
import "AppointmentsDBWorker.dart";
import "AppointmentsList.dart";
import "AppointmentsEntry.dart";
import "AppointmentsModel.dart" show AppointmentsModel, appointmentsModel;
class Appointments extends StatelessWidget {
Appointments() {
print("## Appointments.constructor");
appointmentsModel.loadData("appointments", AppointmentsDBWorker.db);
}
Widget build(BuildContext inContext) {
print("## Appointments.build()");
return ScopedModel<AppointmentsModel>(
model: appointmentsModel,
child: ScopedModelDescendant<AppointmentsModel>(
builder: (BuildContext inContext, Widget inChild,
AppointmentsModel inModel) {
return IndexedStack(
index: inModel.stackIndex,
children: [
AppointmentsList(),
AppointmentsEntry()
]
);
}
)
);
}
}
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "package:path/path.dart";
import "package:sqflite/sqflite.dart";
import "../utils.dart" as utils;
import "AppointmentsModel.dart";
class AppointmentsDBWorker {
AppointmentsDBWorker._();
static final AppointmentsDBWorker db = AppointmentsDBWorker._();
Database _db;
Future get database async {
if (_db == null) {
_db = await init();
}
print("## appointments AppointmentsDBWorker.get-database(): _db = $_db");
return _db;
}
Future<Database> init() async {
String path = join(utils.docsDir.path, "appointments.db");
print("## appointments AppointmentsDBWorker.init(): path = $path");
Database db = await openDatabase(path, version: 1, onOpen: (db) {},
onCreate: (Database inDB, int inVersion) async {
await inDB.execute(
"CREATE TABLE IF NOT EXISTS appointments ("
"id INTEGER PRIMARY KEY,"
"title TEXT,"
"description TEXT,"
"apptDate TEXT,"
"apptTime TEXT"
")"
);
}
);
return db;
}
Appointment appointmentFromMap(Map inMap) {
print(
"## appointments AppointmentsDBWorker.appointmentFromMap(): inMap = $inMap");
Appointment appointment = Appointment();
appointment.id = inMap["id"];
appointment.title = inMap["title"];
appointment.description = inMap["description"];
appointment.apptDate = inMap["apptDate"];
appointment.apptTime = inMap["apptTime"];
print(
"## appointments AppointmentsDBWorker.appointmentFromMap(): appointment = $appointment");
return appointment;
}
Map<String, dynamic> appointmentToMap(Appointment inAppointment) {
print(
"## appointments AppointmentsDBWorker.appointmentToMap(): inAppointment = $inAppointment");
Map<String, dynamic> map = Map<String, dynamic>();
map["id"] = inAppointment.id;
map["title"] = inAppointment.title;
map["description"] = inAppointment.description;
map["apptDate"] = inAppointment.apptDate;
map["apptTime"] = inAppointment.apptTime;
print(
"## appointments AppointmentsDBWorker.appointmentToMap(): map = $map");
return map;
}
Future create(Appointment inAppointment) async {
print(
"## appointments AppointmentsDBWorker.create(): inAppointment = $inAppointment");
Database db = await database;
var val = await db.rawQuery("SELECT MAX(id) + 1 AS id FROM appointments");
int id = val.first["id"];
if (id == null) {
id = 1;
}
return await db.rawInsert(
"INSERT INTO appointments (id, title, description, apptDate, apptTime) VALUES (?, ?, ?, ?, ?)",
[
id,
inAppointment.title,
inAppointment.description,
inAppointment.apptDate,
inAppointment.apptTime
]
);
}
Future<Appointment> get(int inID) async {
print("## appointments AppointmentsDBWorker.get(): inID = $inID");
Database db = await database;
var rec = await db.query(
"appointments", where: "id = ?", whereArgs: [ inID]);
print("## appointments AppointmentsDBWorker.get(): rec.first = $rec.first");
return appointmentFromMap(rec.first);
}
Future<List> getAll() async {
Database db = await database;
var recs = await db.query("appointments");
var list = recs.isNotEmpty
? recs.map((m) => appointmentFromMap(m)).toList()
: [];
print("## appointments AppointmentsDBWorker.getAll(): list = $list");
return list;
}
Future update(Appointment inAppointment) async {
print(
"## appointments AppointmentsDBWorker.update(): inAppointment = $inAppointment");
Database db = await database;
return await db.update(
"appointments", appointmentToMap(inAppointment), where: "id = ?",
whereArgs: [ inAppointment.id]
);
}
Future delete(int inID) async {
print("## appointments AppointmentsDBWorker.delete(): inID = $inID");
Database db = await database;
return await db.delete("appointments", where: "id = ?", whereArgs: [ inID]);
}
}
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "dart:async";
import "package:flutter/material.dart";import "package:scoped_model/scoped_model.dart";
import "../utils.dart" as utils;
import "AppointmentsDBWorker.dart";
import "AppointmentsModel.dart" show AppointmentsModel, appointmentsModel;
class AppointmentsEntry extends StatelessWidget {
final TextEditingController _titleEditingController = TextEditingController();
final TextEditingController _descriptionEditingController = TextEditingController();
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
AppointmentsEntry() {
print("## AppointmentsEntry.constructor");
_titleEditingController.addListener(() {
appointmentsModel.entityBeingEdited.title = _titleEditingController.text;
});
_descriptionEditingController.addListener(() {
appointmentsModel.entityBeingEdited.description =
_descriptionEditingController.text;
});
}
Widget build(BuildContext inContext) {
print("## AppointmentsEntry.build()");
if (appointmentsModel.entityBeingEdited != null) {
_titleEditingController.text = appointmentsModel.entityBeingEdited.title;
_descriptionEditingController.text =
appointmentsModel.entityBeingEdited.description;
}
return ScopedModel(
model: appointmentsModel,
child: ScopedModelDescendant<AppointmentsModel>(
builder: (BuildContext inContext, Widget inChild,
AppointmentsModel inModel) {
return Scaffold(
bottomNavigationBar: Padding(
padding: EdgeInsets.symmetric(
vertical: 0, horizontal: 10),
child: Row(
children: [
FlatButton(
child: Text("Cancel"),
onPressed: () {
FocusScope.of(inContext).requestFocus(
FocusNode());
inModel.setStackIndex(0);
}
),
Spacer(),
FlatButton(
child: Text("Save"),
onPressed: () {
_save(inContext, appointmentsModel);
}
)
]
)
),
body: Form(
key: _formKey,
child: ListView(
children: [
ListTile(
leading: Icon(Icons.subject),
title: TextFormField(
decoration: InputDecoration(
hintText: "Title"),
controller: _titleEditingController,
validator: (String inValue) {
if (inValue.length == 0) {
return "Please enter a title";
}
return null;
}
)
),
ListTile(
leading: Icon(Icons.description),
title: TextFormField(
keyboardType: TextInputType.multiline,
maxLines: 4,
decoration: InputDecoration(
hintText: "Description"),
controller: _descriptionEditingController
)
),
ListTile(
leading: Icon(Icons.today),
title: Text("Date"),
subtitle: Text(
appointmentsModel.chosenDate == null
? ""
: appointmentsModel.chosenDate),
trailing: IconButton(
icon: Icon(Icons.edit),
color: Colors.blue,
onPressed: () async {
String chosenDate = await utils
.selectDate(
inContext, appointmentsModel,
appointmentsModel.entityBeingEdited
.apptDate
);
if (chosenDate != null) {
appointmentsModel.entityBeingEdited
.apptDate = chosenDate;
}
}
)
),
ListTile(
leading: Icon(Icons.alarm),
title: Text("Time"),
subtitle: Text(
appointmentsModel.apptTime == null
? ""
: appointmentsModel.apptTime),
trailing: IconButton(
icon: Icon(Icons.edit),
color: Colors.blue,
onPressed: () => _selectTime(inContext)
)
)
]
)
)
);
}
)
);
}
Future _selectTime(BuildContext inContext) async {
TimeOfDay initialTime = TimeOfDay.now();
if (appointmentsModel.entityBeingEdited.apptTime != null) {
List timeParts = appointmentsModel.entityBeingEdited.apptTime.split(",");
initialTime = TimeOfDay(
hour: int.parse(timeParts[0]), minute: int.parse(timeParts[1]));
}
TimeOfDay picked = await showTimePicker(
context: inContext, initialTime: initialTime);
if (picked != null) {
appointmentsModel.entityBeingEdited.apptTime =
"${picked.hour},${picked.minute}";
appointmentsModel.setApptTime(picked.format(inContext));
}
}
void _save(BuildContext inContext, AppointmentsModel inModel) async {
print("## AppointmentsEntry._save()");
if (!_formKey.currentState.validate()) {
return;
}
if (inModel.entityBeingEdited.id == null) {
print("## AppointmentsEntry._save(): Creating: ${inModel
.entityBeingEdited}");
await AppointmentsDBWorker.db.create(appointmentsModel.entityBeingEdited);
} else {
print("## AppointmentsEntry._save(): Updating: ${inModel
.entityBeingEdited}");
await AppointmentsDBWorker.db.update(appointmentsModel.entityBeingEdited);
}
appointmentsModel.loadData("appointments", AppointmentsDBWorker.db);
inModel.setStackIndex(0);
Scaffold.of(inContext).showSnackBar(
SnackBar(
backgroundColor: Colors.green,
duration: Duration(seconds: 2),
content: Text("Appointment saved")
)
);
}
}
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "package:flutter/material.dart";
import 'package:flutter_slidable/flutter_slidable.dart';
import "package:scoped_model/scoped_model.dart";
import "package:intl/intl.dart";
import "package:flutter_calendar_carousel/flutter_calendar_carousel.dart";
import "package:flutter_calendar_carousel/classes/event.dart";
import "package:flutter_calendar_carousel/classes/event_list.dart";
import "AppointmentsDBWorker.dart";
import "AppointmentsModel.dart" show Appointment, AppointmentsModel, appointmentsModel;
class AppointmentsList extends StatelessWidget {
Widget build(BuildContext inContext) {
print("## AppointmentssList.build()");
EventList<Event> _markedDateMap = EventList();
for (int i = 0; i < appointmentsModel.entityList.length; i++) {
Appointment appointment = appointmentsModel.entityList[i];
List dateParts = appointment.apptDate.split(",");
DateTime apptDate = DateTime(
int.parse(dateParts[0]), int.parse(dateParts[1]),
int.parse(dateParts[2]));
_markedDateMap.add(
apptDate, Event(date: apptDate,
icon: Container(decoration: BoxDecoration(color: Colors.blue)))
);
}
return ScopedModel<AppointmentsModel>(
model: appointmentsModel,
child: ScopedModelDescendant<AppointmentsModel>(
builder: (inContext, inChild, inModel) {
return Scaffold(
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add, color: Colors.white),
onPressed: () async {
appointmentsModel.entityBeingEdited = Appointment();
DateTime now = DateTime.now();
appointmentsModel.entityBeingEdited.apptDate =
"${now.year},${now.month},${now.day}";
appointmentsModel.setChosenDate(
DateFormat.yMMMMd("en_US").format(now.toLocal()));
appointmentsModel.setApptTime(null);
appointmentsModel.setStackIndex(1);
}
),
body: Column(
children: [
Expanded(
child: Container(
margin: EdgeInsets.symmetric(horizontal: 10),
child: CalendarCarousel<Event>(
thisMonthDayBorderColor: Colors.grey,
daysHaveCircularBorder: false,
markedDatesMap: _markedDateMap,
onDayPressed: (DateTime inDate, List<
Event> inEvents) {
_showAppointments(inDate, inContext);
}
)
)
)
]
)
);
}
)
);
}
void _showAppointments(DateTime inDate, BuildContext inContext) async {
print(
"## AppointmentsList._showAppointments(): inDate = $inDate (${inDate
.year},${inDate.month},${inDate.day})"
);
print(
"## AppointmentsList._showAppointments(): appointmentsModel.entityList.length = "
"${appointmentsModel.entityList.length}");
print(
"## AppointmentsList._showAppointments(): appointmentsModel.entityList = "
"${appointmentsModel.entityList}");
showModalBottomSheet(
context: inContext,
builder: (BuildContext inContext) {
return ScopedModel<AppointmentsModel>(
model: appointmentsModel,
child: ScopedModelDescendant<AppointmentsModel>(
builder: (BuildContext inContext, Widget inChild,
AppointmentsModel inModel) {
return Scaffold(
body: Container(
child: Padding(
padding: EdgeInsets.all(10),
child: GestureDetector(
child: Column(
children: [
Text(
DateFormat.yMMMMd("en_US").format(
inDate.toLocal()),
textAlign: TextAlign.center,
style: TextStyle(color: Theme
.of(inContext)
.accentColor, fontSize: 24)
),
Divider(),
Expanded(
child: ListView.builder(
itemCount: appointmentsModel
.entityList.length,
itemBuilder: (
BuildContext inBuildContext,
int inIndex) {
Appointment appointment = appointmentsModel
.entityList[inIndex];
print(
"## AppointmentsList._showAppointments().ListView.builder(): "
"appointment = $appointment");
if (appointment.apptDate !=
"${inDate
.year},${inDate
.month},${inDate
.day}") {
return Container(
height: 0);
}
print(
"## AppointmentsList._showAppointments().ListView.builder(): "
"INCLUDING appointment = $appointment");
String apptTime = "";
if (appointment.apptTime !=
null) {
List timeParts = appointment
.apptTime.split(
",");
TimeOfDay at = TimeOfDay(
hour: int.parse(
timeParts[0]),
minute: int.parse(
timeParts[1])
);
apptTime =
" (${at.format(
inContext)})";
}
return Slidable(
actionPane: SlidableDrawerActionPane(),
actionExtentRatio: .25,
child: Container(
margin: EdgeInsets
.only(
bottom: 8),
color: Colors.grey
.shade300,
child: ListTile(
title: Text(
"${appointment
.title}$apptTime"),
subtitle: appointment
.description ==
null ?
null : Text(
"${appointment
.description}"),
onTap: () async {
_editAppointment(
inContext,
appointment);
}
)
),
secondaryActions: [
IconSlideAction(
caption: "Delete",
color: Colors.red,
icon: Icons
.delete,
onTap: () =>
_deleteAppointment(
inBuildContext,
appointment)
)
]
);
}
)
)
]
)
)
)
)
);
}
)
);
}
);
}
void _editAppointment(BuildContext inContext,
Appointment inAppointment) async {
print(
"## AppointmentsList._editAppointment(): inAppointment = $inAppointment");
appointmentsModel.entityBeingEdited =
await AppointmentsDBWorker.db.get(inAppointment.id);
if (appointmentsModel.entityBeingEdited.apptDate == null) {
appointmentsModel.setChosenDate(null);
} else {
List dateParts = appointmentsModel.entityBeingEdited.apptDate.split(",");
DateTime apptDate = DateTime(
int.parse(dateParts[0]), int.parse(dateParts[1]),
int.parse(dateParts[2])
);
appointmentsModel.setChosenDate(
DateFormat.yMMMMd("en_US").format(apptDate.toLocal())
);
}
if (appointmentsModel.entityBeingEdited.apptTime == null) {
appointmentsModel.setApptTime(null);
} else {
List timeParts = appointmentsModel.entityBeingEdited.apptTime.split(",");
TimeOfDay apptTime = TimeOfDay(
hour: int.parse(timeParts[0]), minute: int.parse(timeParts[1])
);
appointmentsModel.setApptTime(apptTime.format(inContext));
}
appointmentsModel.setStackIndex(1);
Navigator.pop(inContext);
}
Future _deleteAppointment(BuildContext inContext,
Appointment inAppointment) async {
print(
"## AppointmentsList._deleteAppointment(): inAppointment = $inAppointment");
return showDialog(
context: inContext,
barrierDismissible: false,
builder: (BuildContext inAlertContext) {
return AlertDialog(
title: Text("Delete Appointment"),
content: Text(
"Are you sure you want to delete ${inAppointment.title}?"),
actions: [
FlatButton(child: Text("Cancel"),
onPressed: () {
Navigator.of(inAlertContext).pop();
}
),
FlatButton(child: Text("Delete"),
onPressed: () async {
await AppointmentsDBWorker.db.delete(inAppointment.id);
Navigator.of(inAlertContext).pop();
Scaffold.of(inContext).showSnackBar(
SnackBar(
backgroundColor: Colors.red,
duration: Duration(seconds: 2),
content: Text("Appointment deleted")
)
);
appointmentsModel.loadData(
"appointments", AppointmentsDBWorker.db);
}
)
]
);
}
);
}
}
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "../BaseModel.dart";
class Appointment {
int id;
String title;
String description;
String apptDate;
String apptTime;
String toString() {
return "{ id=$id, title=$title, description=$description, apptDate=$apptDate, apptDate=$apptTime }";
}
}
class AppointmentsModel extends BaseModel {
String apptTime;
void setApptTime(String inApptTime) {
apptTime = inApptTime;
notifyListeners();
}
}
AppointmentsModel appointmentsModel = AppointmentsModel();
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "package:scoped_model/scoped_model.dart";
class BaseModel extends Model {
int stackIndex = 0;
List entityList = [];
var entityBeingEdited;
String chosenDate;
void setChosenDate(String inDate) {
print("## BaseModel.setChosenDate(): inDate = $inDate");
chosenDate = inDate;
notifyListeners();
}
void loadData(String inEntityType, dynamic inDatabase) async {
print("## ${inEntityType}Model.loadData()");
entityList = await inDatabase.getAll();
notifyListeners();
}
void setStackIndex(int inStackIndex) {
print("## BaseModel.setStackIndex(): inStackIndex = $inStackIndex");
stackIndex = inStackIndex;
notifyListeners();
}
}
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "dart:async";
import "dart:io";
import "package:flutter/material.dart";
import "package:path/path.dart";
import "package:scoped_model/scoped_model.dart";
import "package:image_picker/image_picker.dart";
import "../utils.dart" as utils;
import "ContactsDBWorker.dart";
import "ContactsModel.dart" show ContactsModel, contactsModel;
class ContactsEntry extends StatelessWidget {
final TextEditingController _nameEditingController = TextEditingController();
final TextEditingController _phoneEditingController = TextEditingController();
final TextEditingController _emailEditingController = TextEditingController();
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
ContactsEntry() {
print("## ContactsEntry.constructor");
_nameEditingController.addListener(() {
contactsModel.entityBeingEdited.name = _nameEditingController.text;
});
_phoneEditingController.addListener(() {
contactsModel.entityBeingEdited.phone = _phoneEditingController.text;
});
_emailEditingController.addListener(() {
contactsModel.entityBeingEdited.email = _emailEditingController.text;
});
}
Widget build(BuildContext inContext) {
print("## ContactsEntry.build()");
if (contactsModel.entityBeingEdited != null) {
_nameEditingController.text = contactsModel.entityBeingEdited.name;
_phoneEditingController.text = contactsModel.entityBeingEdited.phone;
_emailEditingController.text = contactsModel.entityBeingEdited.email;
}
return ScopedModel(
model: contactsModel,
child: ScopedModelDescendant<ContactsModel>(
builder: (BuildContext inContext, Widget inChild,
ContactsModel inModel) {
File avatarFile = File(join(utils.docsDir.path, "avatar"));
if (avatarFile.existsSync() == false) {
if (inModel.entityBeingEdited != null &&
inModel.entityBeingEdited.id != null) {
avatarFile = File(join(
utils.docsDir.path,
inModel.entityBeingEdited.id.toString()));
}
}
return Scaffold(
bottomNavigationBar: Padding(
padding: EdgeInsets.symmetric(
vertical: 0, horizontal: 10),
child: Row(
children: [
FlatButton(
child: Text("Cancel"),
onPressed: () {
File avatarFile = File(
join(utils.docsDir.path, "avatar"));
if (avatarFile.existsSync()) {
avatarFile.deleteSync();
}
FocusScope.of(inContext).requestFocus(
FocusNode());
inModel.setStackIndex(0);
}
),
Spacer(),
FlatButton(
child: Text("Save"),
onPressed: () {
_save(inContext, inModel);
}
)
]
)),
body: Form(
key: _formKey,
child: ListView(
children: [
ListTile(
title: avatarFile.existsSync()
? Image.file(avatarFile)
: Text("No avatar image for this contact"),
trailing: IconButton(
icon: Icon(Icons.edit),
color: Colors.blue,
onPressed: () => _selectAvatar(inContext)
)
),
ListTile(
leading: Icon(Icons.person),
title: TextFormField(
decoration: InputDecoration(
hintText: "Name"),
controller: _nameEditingController,
validator: (String inValue) {
if (inValue.length == 0) {
return "Please enter a name";
}
return null;
}
)
),
ListTile(
leading: Icon(Icons.phone),
title: TextFormField(
keyboardType: TextInputType.phone,
decoration: InputDecoration(
hintText: "Phone"),
controller: _phoneEditingController
)
),
ListTile(
leading: Icon(Icons.email),
title: TextFormField(
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
hintText: "Email"),
controller: _emailEditingController
)
),
ListTile(
leading: Icon(Icons.today),
title: Text("Birthday"),
subtitle: Text(contactsModel.chosenDate == null
? ""
: contactsModel.chosenDate),
trailing: IconButton(
icon: Icon(Icons.edit),
color: Colors.blue,
onPressed: () async {
String chosenDate = await utils
.selectDate(
inContext, contactsModel,
contactsModel.entityBeingEdited
.birthday
);
if (chosenDate != null) {
contactsModel.entityBeingEdited
.birthday =
chosenDate;
}
}
)
)
]
)
)
);
}
)
);
}
Future _selectAvatar(BuildContext inContext) {
print("ContactsEntry._selectAvatar()");
return showDialog(context: inContext,
builder: (BuildContext inDialogContext) {
return AlertDialog(
content: SingleChildScrollView(
child: ListBody(
children: [
GestureDetector(
child: Text("Take a picture"),
onTap: () async {
var cameraImage = await ImagePicker.pickImage(
source: ImageSource.camera);
if (cameraImage != null) {
cameraImage.copySync(
join(utils.docsDir.path, "avatar"));
contactsModel.triggerRebuild();
}
Navigator.of(inDialogContext).pop();
}
),
Padding(padding: EdgeInsets.all(10)),
GestureDetector(
child: Text("Select From Gallery"),
onTap: () async {
var galleryImage = await ImagePicker.pickImage(
source: ImageSource.gallery);
if (galleryImage != null) {
galleryImage.copySync(
join(utils.docsDir.path, "avatar"));
contactsModel.triggerRebuild();
}
Navigator.of(inDialogContext).pop();
}
)
]
)
)
);
}
);
}
void _save(BuildContext inContext, ContactsModel inModel) async {
print("## ContactsEntry._save()");
if (!_formKey.currentState.validate()) {
return;
}
var id;
if (inModel.entityBeingEdited.id == null) {
print("## ContactsEntry._save(): Creating: ${inModel.entityBeingEdited}");
id = await ContactsDBWorker.db.create(contactsModel.entityBeingEdited);
} else {
print("## ContactsEntry._save(): Updating: ${inModel.entityBeingEdited}");
id = contactsModel.entityBeingEdited.id;
await ContactsDBWorker.db.update(contactsModel.entityBeingEdited);
}
File avatarFile = File(join(utils.docsDir.path, "avatar"));
if (avatarFile.existsSync()) {
print("## ContactsEntry._save(): Renaming avatar file to id = $id");
avatarFile.renameSync(join(utils.docsDir.path, id.toString()));
}
contactsModel.loadData("contacts", ContactsDBWorker.db);
inModel.setStackIndex(0);
Scaffold.of(inContext).showSnackBar(
SnackBar(
backgroundColor: Colors.green,
duration: Duration(seconds: 2),
content: Text("Contact saved")
)
);
}
}
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "dart:io";
import "package:flutter/material.dart";import "package:path_provider/path_provider.dart";
import "appointments/Appointments.dart";
import "contacts/Contacts.dart";
import "notes/Notes.dart";
import "tasks/Tasks.dart";
import "utils.dart" as utils;
void main() {
WidgetsFlutterBinding.ensureInitialized();
print("## main(): FlutterBook Starting");
startMeUp() async {
Directory docsDir = await getApplicationDocumentsDirectory();
utils.docsDir = docsDir;
runApp(FlutterBook());
}
startMeUp();
}
class FlutterBook extends StatelessWidget {
Widget build(BuildContext inContext) {
print("## FlutterBook.build()");
return MaterialApp(
home: DefaultTabController(
length: 4,
child: Scaffold(
appBar: AppBar(
title: Text("FlutterBook"),
bottom: TabBar(
tabs: [
Tab(icon: Icon(Icons.date_range),
text: "Appointments"),
Tab(icon: Icon(Icons.contacts), text: "Contacts"),
Tab(icon: Icon(Icons.note), text: "Notes"),
Tab(icon: Icon(Icons.assignment_turned_in),
text: "Tasks")
]
)
),
body: TabBarView(
children: [
Appointments(),
Contacts(),
Notes(),
Tasks()
]
)
)
)
);
}
}
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "package:flutter/material.dart";
import "package:scoped_model/scoped_model.dart";
import "NotesDBWorker.dart";
import "NotesList.dart";
import "NotesEntry.dart";
import "NotesModel.dart" show NotesModel, notesModel;
class Notes extends StatelessWidget {
Notes() {
print("## Notes.constructor");
notesModel.loadData("notes", NotesDBWorker.db);
}
Widget build(BuildContext inContext) {
print("## Notes.build()");
return ScopedModel<NotesModel>(
model: notesModel,
child: ScopedModelDescendant<NotesModel>(
builder: (BuildContext inContext, Widget inChild,
NotesModel inModel) {
return IndexedStack(
index: inModel.stackIndex,
children: [
NotesList(),
NotesEntry()
]
);
}
)
);
}
}
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "package:path/path.dart";
import "package:sqflite/sqflite.dart";
import "../utils.dart" as utils;
import "NotesModel.dart";
class NotesDBWorker {
NotesDBWorker._();
static final NotesDBWorker db = NotesDBWorker._();
Database _db;
Future get database async {
if (_db == null) {
_db = await init();
}
print("## Notes NotesDBWorker.get-database(): _db = $_db");
return _db;
}
Future<Database> init() async {
print("Notes NotesDBWorker.init()");
String path = join(utils.docsDir.path, "notes.db");
print("## notes NotesDBWorker.init(): path = $path");
Database db = await openDatabase(path, version: 1, onOpen: (db) {},
onCreate: (Database inDB, int inVersion) async {
await inDB.execute(
"CREATE TABLE IF NOT EXISTS notes ("
"id INTEGER PRIMARY KEY,"
"title TEXT,"
"content TEXT,"
"color TEXT"
")"
);
}
);
return db;
}
Note noteFromMap(Map inMap) {
print("## Notes NotesDBWorker.noteFromMap(): inMap = $inMap");
Note note = Note();
note.id = inMap["id"];
note.title = inMap["title"];
note.content = inMap["content"];
note.color = inMap["color"];
print("## Notes NotesDBWorker.noteFromMap(): note = $note");
return note;
}
Map<String, dynamic> noteToMap(Note inNote) {
print("## Notes NotesDBWorker.noteToMap(): inNote = $inNote");
Map<String, dynamic> map = Map<String, dynamic>();
map["id"] = inNote.id;
map["title"] = inNote.title;
map["content"] = inNote.content;
map["color"] = inNote.color;
print("## notes NotesDBWorker.noteToMap(): map = $map");
return map;
}
Future create(Note inNote) async {
print("## Notes NotesDBWorker.create(): inNote = $inNote");
Database db = await database;
var val = await db.rawQuery("SELECT MAX(id) + 1 AS id FROM notes");
int id = val.first["id"];
if (id == null) {
id = 1;
}
return await db.rawInsert(
"INSERT INTO notes (id, title, content, color) VALUES (?, ?, ?, ?)",
[
id,
inNote.title,
inNote.content,
inNote.color
]
);
}
Future<Note> get(int inID) async {
print("## Notes NotesDBWorker.get(): inID = $inID");
Database db = await database;
var rec = await db.query("notes", where: "id = ?", whereArgs: [ inID]);
print("## Notes NotesDBWorker.get(): rec.first = $rec.first");
return noteFromMap(rec.first);
}
Future<List> getAll() async {
print("## Notes NotesDBWorker.getAll()");
Database db = await database;
var recs = await db.query("notes");
var list = recs.isNotEmpty ? recs.map((m) => noteFromMap(m)).toList() : [];
print("## Notes NotesDBWorker.getAll(): list = $list");
return list;
}
Future update(Note inNote) async {
print("## Notes NotesDBWorker.update(): inNote = $inNote");
Database db = await database;
return await db.update(
"notes", noteToMap(inNote), where: "id = ?", whereArgs: [ inNote.id]);
}
Future delete(int inID) async {
print("## Notes NotesDBWorker.delete(): inID = $inID");
Database db = await database;
return await db.delete("notes", where: "id = ?", whereArgs: [ inID]);
}
}
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "package:flutter/material.dart";
import "package:scoped_model/scoped_model.dart";
import "NotesDBWorker.dart";
import "NotesModel.dart" show NotesModel, notesModel;
class NotesEntry extends StatelessWidget {
final TextEditingController _titleEditingController = TextEditingController();
final TextEditingController _contentEditingController = TextEditingController();
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
NotesEntry() {
print("## NotesEntry.constructor");
_titleEditingController.addListener(() {
notesModel.entityBeingEdited.title = _titleEditingController.text;
});
_contentEditingController.addListener(() {
notesModel.entityBeingEdited.content = _contentEditingController.text;
});
}
Widget build(BuildContext inContext) {
print("## NotesEntry.build()");
if (notesModel.entityBeingEdited != null) {
_titleEditingController.text = notesModel.entityBeingEdited.title;
_contentEditingController.text = notesModel.entityBeingEdited.content;
}
return ScopedModel(
model: notesModel,
child: ScopedModelDescendant<NotesModel>(
builder: (BuildContext inContext, Widget inChild,
NotesModel inModel) {
return Scaffold(
bottomNavigationBar: Padding(
padding: EdgeInsets.symmetric(
vertical: 0, horizontal: 10),
child: Row(
children: [
FlatButton(
child: Text("Cancel"),
onPressed: () {
FocusScope.of(inContext).requestFocus(
FocusNode());
inModel.setStackIndex(0);
}
),
Spacer(),
FlatButton(
child: Text("Save"),
onPressed: () {
_save(inContext, notesModel);
}
)
]
)
),
body: Form(
key: _formKey,
child: ListView(
children: [
ListTile(
leading: Icon(Icons.title),
title: TextFormField(
decoration: InputDecoration(
hintText: "Title"),
controller: _titleEditingController,
validator: (String inValue) {
if (inValue.length == 0) {
return "Please enter a title";
}
return null;
}
)
),
ListTile(
leading: Icon(Icons.content_paste),
title: TextFormField(
keyboardType: TextInputType.multiline,
maxLines: 8,
decoration: InputDecoration(
hintText: "Content"),
controller: _contentEditingController,
validator: (String inValue) {
if (inValue.length == 0) {
return "Please enter content";
}
return null;
}
)
),
ListTile(
leading: Icon(Icons.color_lens),
title: Row(
children: [
GestureDetector(
child: Container(
decoration: ShapeDecoration(shape:
Border.all(color: Colors.red,
width: 18) +
Border.all(
width: 6,
color: notesModel.color ==
"red"
? Colors.red
: Theme
.of(inContext)
.canvasColor
)
)
),
onTap: () {
notesModel.entityBeingEdited.color =
"red";
notesModel.setColor("red");
}
),
Spacer(),
GestureDetector(
child: Container(
decoration: ShapeDecoration(shape:
Border.all(color: Colors.green,
width: 18) +
Border.all(
width: 6,
color: notesModel.color ==
"green"
? Colors.green
: Theme
.of(inContext)
.canvasColor
)
)
),
onTap: () {
notesModel.entityBeingEdited.color =
"green";
notesModel.setColor("green");
}
),
Spacer(),
GestureDetector(
child: Container(
decoration: ShapeDecoration(shape:
Border.all(color: Colors.blue,
width: 18) +
Border.all(
width: 6,
color: notesModel.color ==
"blue"
? Colors.blue
: Theme
.of(inContext)
.canvasColor
)
)
),
onTap: () {
notesModel.entityBeingEdited.color =
"blue";
notesModel.setColor("blue");
}
),
Spacer(),
GestureDetector(
child: Container(
decoration: ShapeDecoration(shape:
Border.all(color: Colors.yellow,
width: 18) +
Border.all(
width: 6,
color: notesModel.color ==
"yellow"
? Colors.yellow
: Theme
.of(inContext)
.canvasColor
)
)
),
onTap: () {
notesModel.entityBeingEdited.color =
"yellow";
notesModel.setColor("yellow");
}
),
Spacer(),
GestureDetector(
child: Container(
decoration: ShapeDecoration(shape:
Border.all(color: Colors.grey,
width: 18) +
Border.all(
width: 6,
color: notesModel.color ==
"grey"
? Colors.grey
: Theme
.of(inContext)
.canvasColor
)
)
),
onTap: () {
notesModel.entityBeingEdited.color =
"grey";
notesModel.setColor("grey");
}
),
Spacer(),
GestureDetector(
child: Container(
decoration: ShapeDecoration(shape:
Border.all(color: Colors.purple,
width: 18) +
Border.all(
width: 6,
color: notesModel.color ==
"purple"
? Colors.purple
: Theme
.of(inContext)
.canvasColor
)
)
),
onTap: () {
notesModel.entityBeingEdited.color =
"purple";
notesModel.setColor("purple");
}
)
]
)
)
]
)
)
);
}
)
);
}
void _save(BuildContext inContext, NotesModel inModel) async {
print("## NotesEntry._save()");
if (!_formKey.currentState.validate()) {
return;
}
if (inModel.entityBeingEdited.id == null) {
print("## NotesEntry._save(): Creating: ${inModel.entityBeingEdited}");
await NotesDBWorker.db.create(notesModel.entityBeingEdited);
} else {
print("## NotesEntry._save(): Updating: ${inModel.entityBeingEdited}");
await NotesDBWorker.db.update(notesModel.entityBeingEdited);
}
notesModel.loadData("notes", NotesDBWorker.db);
inModel.setStackIndex(0);
Scaffold.of(inContext).showSnackBar(
SnackBar(
backgroundColor: Colors.green,
duration: Duration(seconds: 2),
content: Text("Note saved")
)
);
}
}
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "package:flutter/material.dart";
import "package:scoped_model/scoped_model.dart";
import "package:flutter_slidable/flutter_slidable.dart";
import "NotesDBWorker.dart";
import "NotesModel.dart" show Note, NotesModel, notesModel;
class NotesList extends StatelessWidget {
Widget build(BuildContext inContext) {
print("## NotesList.build()");
return ScopedModel<NotesModel>(
model: notesModel,
child: ScopedModelDescendant<NotesModel>(
builder: (BuildContext inContext, Widget inChild,
NotesModel inModel) {
return Scaffold(
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add, color: Colors.white),
onPressed: () async {
notesModel.entityBeingEdited = Note();
notesModel.setColor(null);
notesModel.setStackIndex(1);
}
),
body: ListView.builder(
itemCount: notesModel.entityList.length,
itemBuilder: (BuildContext inBuildContext, int inIndex) {
Note note = notesModel.entityList[inIndex];
Color color = Colors.white;
switch (note.color) {
case "red" :
color = Colors.red;
break;
case "green" :
color = Colors.green;
break;
case "blue" :
color = Colors.blue;
break;
case "yellow" :
color = Colors.yellow;
break;
case "grey" :
color = Colors.grey;
break;
case "purple" :
color = Colors.purple;
break;
}
return Container(
padding: EdgeInsets.fromLTRB(20, 20, 20, 0),
child: Slidable(
actionPane: SlidableDrawerActionPane(),
actionExtentRatio: .25,
secondaryActions: [
IconSlideAction(
caption: "Delete",
color: Colors.red,
icon: Icons.delete,
onTap: () => _deleteNote(inContext, note)
)
],
child: Card(
elevation: 8,
color: color,
child: ListTile(
title: Text("${note.title}"),
subtitle: Text("${note.content}"),
onTap: () async {
notesModel.entityBeingEdited =
await NotesDBWorker.db.get(note.id);
notesModel.setColor(
notesModel.entityBeingEdited
.color);
notesModel.setStackIndex(1);
}
)
)
)
);
}
)
);
}
)
);
}
Future _deleteNote(BuildContext inContext, Note inNote) async {
print("## NotestList._deleteNote(): inNote = $inNote");
return showDialog(
context: inContext,
barrierDismissible: false,
builder: (BuildContext inAlertContext) {
return AlertDialog(
title: Text("Delete Note"),
content: Text("Are you sure you want to delete ${inNote.title}?"),
actions: [
FlatButton(child: Text("Cancel"),
onPressed: () {
Navigator.of(inAlertContext).pop();
}
),
FlatButton(child: Text("Delete"),
onPressed: () async {
await NotesDBWorker.db.delete(inNote.id);
Navigator.of(inAlertContext).pop();
Scaffold.of(inContext).showSnackBar(
SnackBar(
backgroundColor: Colors.red,
duration: Duration(seconds: 2),
content: Text("Note deleted")
)
);
notesModel.loadData("notes", NotesDBWorker.db);
}
)
]
);
}
);
}
}
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "../BaseModel.dart";
class Note {
int id;
String title;
String content;
String color;
String toString() {
return "{ id=$id, title=$title, content=$content, color=$color }";
}
}
class NotesModel extends BaseModel {
String color;
void setColor(String inColor) {
print("## NotesModel.setColor(): inColor = $inColor");
color = inColor;
notifyListeners();
}
}
NotesModel notesModel = NotesModel();
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "package:flutter/material.dart";import "package:scoped_model/scoped_model.dart";
import "TasksDBWorker.dart";
import "TasksList.dart";
import "TasksEntry.dart";
import "TasksModel.dart" show TasksModel, tasksModel;
class Tasks extends StatelessWidget {
Tasks() {
print("## Tasks.constructor");
tasksModel.loadData("tasks", TasksDBWorker.db);
}
Widget build(BuildContext inContext) {
print("## Tasks.build()");
return ScopedModel<TasksModel>(
model: tasksModel,
child: ScopedModelDescendant<TasksModel>(
builder: (BuildContext inContext, Widget inChild,
TasksModel inModel) {
return IndexedStack(
index: inModel.stackIndex,
children: [
TasksList(),
TasksEntry()
]
);
}
)
);
}
}
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "package:path/path.dart";
import "package:sqflite/sqflite.dart";
import "../utils.dart" as utils;
import "TasksModel.dart";
class TasksDBWorker {
TasksDBWorker._();
static final TasksDBWorker db = TasksDBWorker._();
Database _db;
Future get database async {
if (_db == null) {
_db = await init();
}
print("## tasks TasksDBWorker.get-database(): _db = $_db");
return _db;
}
Future<Database> init() async {
print("## Tasks TasksDBWorker.init()");
String path = join(utils.docsDir.path, "tasks.db");
print("## tasks TasksDBWorker.init(): path = $path");
Database db = await openDatabase(path, version: 1, onOpen: (db) {},
onCreate: (Database inDB, int inVersion) async {
await inDB.execute(
"CREATE TABLE IF NOT EXISTS tasks ("
"id INTEGER PRIMARY KEY,"
"description TEXT,"
"dueDate TEXT,"
"completed TEXT"
")"
);
}
);
return db;
}
Task taskFromMap(Map inMap) {
print("## Tasks TasksDBWorker.taskFromMap(): inMap = $inMap");
Task task = Task();
task.id = inMap["id"];
task.description = inMap["description"];
task.dueDate = inMap["dueDate"];
task.completed = inMap["completed"];
print("## Tasks TasksDBWorker.taskFromMap(): task = $task");
return task;
}
Map<String, dynamic> taskToMap(Task inTask) {
print("## tasks TasksDBWorker.taskToMap(): inTask = $inTask");
Map<String, dynamic> map = Map<String, dynamic>();
map["id"] = inTask.id;
map["description"] = inTask.description;
map["dueDate"] = inTask.dueDate;
map["completed"] = inTask.completed;
print("## tasks TasksDBWorker.taskToMap(): map = $map");
return map;
}
Future create(Task inTask) async {
print("## Tasks TasksDBWorker.create(): inTask = $inTask");
Database db = await database;
var val = await db.rawQuery("SELECT MAX(id) + 1 AS id FROM tasks");
int id = val.first["id"];
if (id == null) {
id = 1;
}
return await db.rawInsert(
"INSERT INTO tasks (id, description, dueDate, completed) VALUES (?, ?, ?, ?)",
[
id,
inTask.description,
inTask.dueDate,
inTask.completed
]
);
}
Future<Task> get(int inID) async {
print("## Tasks TasksDBWorker.get(): inID = $inID");
Database db = await database;
var rec = await db.query("tasks", where: "id = ?", whereArgs: [ inID]);
print("## Tasks TasksDBWorker.get(): rec.first = $rec.first");
return taskFromMap(rec.first);
}
Future<List> getAll() async {
print("## Tasks TasksDBWorker.getAll()");
Database db = await database;
var recs = await db.query("tasks");
var list = recs.isNotEmpty ? recs.map((m) => taskFromMap(m)).toList() : [];
print("## Tasks TasksDBWorker.getAll(): list = $list");
return list;
}
Future update(Task inTask) async {
print("## Tasks TasksDBWorker.update(): inTask = $inTask");
Database db = await database;
return await db.update(
"tasks", taskToMap(inTask), where: "id = ?", whereArgs: [ inTask.id]);
}
Future delete(int inID) async {
print("## Taasks TasksDBWorker.delete(): inID = $inID");
Database db = await database;
return await db.delete("Tasks", where: "id = ?", whereArgs: [ inID]);
}
}
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "package:flutter/material.dart";
import "package:scoped_model/scoped_model.dart";
import "../utils.dart" as utils;
import "TasksDBWorker.dart";
import "TasksModel.dart" show TasksModel, tasksModel;
class TasksEntry extends StatelessWidget {
final TextEditingController _descriptionEditingController = TextEditingController();
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
TasksEntry() {
print("## TasksList.constructor");
_descriptionEditingController.addListener(() {
tasksModel.entityBeingEdited.description =
_descriptionEditingController.text;
});
}
Widget build(BuildContext inContext) {
print("## TasksEntry.build()");
if (tasksModel.entityBeingEdited != null) {
_descriptionEditingController.text =
tasksModel.entityBeingEdited.description;
}
return ScopedModel(
model: tasksModel,
child: ScopedModelDescendant<TasksModel>(
builder: (BuildContext inContext, Widget inChild,
TasksModel inModel) {
return Scaffold(
bottomNavigationBar: Padding(
padding: EdgeInsets.symmetric(
vertical: 0, horizontal: 10),
child: Row(
children: [
FlatButton(child: Text("Cancel"),
onPressed: () {
FocusScope.of(inContext).requestFocus(
FocusNode());
inModel.setStackIndex(0);
}
),
Spacer(),
FlatButton(child: Text("Save"),
onPressed: () {
_save(inContext, tasksModel);
}
)
]
)
),
body: Form(
key: _formKey,
child: ListView(
children: [
ListTile(
leading: Icon(Icons.description),
title: TextFormField(
keyboardType: TextInputType.multiline,
maxLines: 4,
decoration: InputDecoration(
hintText: "Description"),
controller: _descriptionEditingController,
validator: (String inValue) {
if (inValue.length == 0) {
return "Please enter a description";
}
return null;
}
)
),
ListTile(
leading: Icon(Icons.today),
title: Text("Due Date"),
subtitle: Text(
tasksModel.chosenDate == null
? ""
: tasksModel
.chosenDate),
trailing: IconButton(
icon: Icon(Icons.edit), color: Colors.blue,
onPressed: () async {
String chosenDate = await utils
.selectDate(
inContext, tasksModel,
tasksModel.entityBeingEdited.dueDate
);
if (chosenDate != null) {
tasksModel.entityBeingEdited.dueDate =
chosenDate;
}
}
)
)
]
)
)
);
}
)
);
}
void _save(BuildContext inContext, TasksModel inModel) async {
print("## TasksEntry._save()");
if (!_formKey.currentState.validate()) {
return;
}
if (inModel.entityBeingEdited.id == null) {
print("## TasksEntry._save(): Creating: ${inModel.entityBeingEdited}");
await TasksDBWorker.db.create(tasksModel.entityBeingEdited);
} else {
print("## TasksEntry._save(): Updating: ${inModel.entityBeingEdited}");
await TasksDBWorker.db.update(tasksModel.entityBeingEdited);
}
tasksModel.loadData("tasks", TasksDBWorker.db);
inModel.setStackIndex(0);
Scaffold.of(inContext).showSnackBar(
SnackBar(
backgroundColor: Colors.green,
duration: Duration(seconds: 2),
content: Text("Task saved")
)
);
}
}
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "package:flutter/material.dart";import "package:scoped_model/scoped_model.dart";
import "package:flutter_slidable/flutter_slidable.dart";
import "package:intl/intl.dart";
import "TasksDBWorker.dart";
import "TasksModel.dart" show Task, TasksModel, tasksModel;
class TasksList extends StatelessWidget {
Widget build(BuildContext inContext) {
print("## TasksList.build()");
return ScopedModel<TasksModel>(
model: tasksModel,
child: ScopedModelDescendant<TasksModel>(
builder: (BuildContext inContext, Widget inChild,
TasksModel inModel) {
return Scaffold(
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add, color: Colors.white),
onPressed: () async {
tasksModel.entityBeingEdited = Task();
tasksModel.setChosenDate(null);
tasksModel.setStackIndex(1);
}
),
body: ListView.builder(
padding: EdgeInsets.fromLTRB(0, 10, 0, 0),
itemCount: tasksModel.entityList.length,
itemBuilder: (BuildContext inBuildContext, int inIndex) {
Task task = tasksModel.entityList[inIndex];
String sDueDate;
if (task.dueDate != null) {
List dateParts = task.dueDate.split(",");
DateTime dueDate = DateTime(
int.parse(dateParts[0]), int.parse(dateParts[1]),
int.parse(dateParts[2])
);
sDueDate =
DateFormat.yMMMMd("en_US").format(
dueDate.toLocal());
}
return Slidable(
actionPane: SlidableDrawerActionPane(),
actionExtentRatio: .25,
child: ListTile(
leading: Checkbox(
value: task.completed == "true"
? true
: false,
onChanged: (inValue) async {
task.completed = inValue.toString();
await TasksDBWorker.db.update(task);
tasksModel.loadData(
"tasks", TasksDBWorker.db);
}
),
title: Text(
"${task.description}",
style: task.completed == "true"
?
TextStyle(color: Theme
.of(inContext)
.disabledColor,
decoration: TextDecoration.lineThrough)
:
TextStyle(color: Theme
.of(inContext)
.textTheme
.title
.color)
),
subtitle: task.dueDate == null ?
null :
Text(
sDueDate,
style: task.completed == "true" ?
TextStyle(color: Theme
.of(inContext)
.disabledColor,
decoration: TextDecoration.lineThrough)
:
TextStyle(color: Theme
.of(inContext)
.textTheme
.title
.color)
),
onTap: () async {
if (task.completed == "true") {
return;
}
tasksModel.entityBeingEdited =
await TasksDBWorker.db.get(task.id);
if (tasksModel.entityBeingEdited.dueDate ==
null) {
tasksModel.setChosenDate(null);
} else {
tasksModel.setChosenDate(sDueDate);
}
tasksModel.setStackIndex(1);
}
),
secondaryActions: [
IconSlideAction(
caption: "Delete",
color: Colors.red,
icon: Icons.delete,
onTap: () => _deleteTask(inContext, task)
)
]
);
}
)
);
}
)
);
}
Future _deleteTask(BuildContext inContext, Task inTask) async {
print("## TasksList._deleteTask(): inTask = $inTask");
return showDialog(
context: inContext,
barrierDismissible: false,
builder: (BuildContext inAlertContext) {
return AlertDialog(
title: Text("Delete Task"),
content: Text(
"Are you sure you want to delete ${inTask.description}?"),
actions: [
FlatButton(child: Text("Cancel"),
onPressed: () {
Navigator.of(inAlertContext).pop();
}
),
FlatButton(child: Text("Delete"),
onPressed: () async {
await TasksDBWorker.db.delete(inTask.id);
Navigator.of(inAlertContext).pop();
Scaffold.of(inContext).showSnackBar(
SnackBar(
backgroundColor: Colors.red,
duration: Duration(seconds: 2),
content: Text("Task deleted")
)
);
tasksModel.loadData("tasks", TasksDBWorker.db);
}
)
]
);
}
);
}
}
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "../BaseModel.dart";
class Task {
int id;
String description;
String dueDate;
String completed = "false";
String toString() {
return "{ id=$id, description=$description, dueDate=$dueDate, completed=$completed }";
}
}
class TasksModel extends BaseModel {
}
TasksModel tasksModel = TasksModel();
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "dart:io";import "package:flutter/material.dart";
import "package:intl/intl.dart";
import "BaseModel.dart";
Directory docsDir;
Future selectDate(BuildContext inContext, BaseModel inModel, String inDateString) async {
print("## globals.selectDate()");
DateTime initialDate = DateTime.now();
if (inDateString != null) {
List dateParts = inDateString.split(",");
initialDate = DateTime(int.parse(dateParts[0]), int.parse(dateParts[1]),
int.parse(dateParts[2]));
}
DateTime picked = await showDatePicker(
context: inContext,
initialDate: initialDate,
firstDate: DateTime(1900),
lastDate: DateTime(2100)
);
if (picked != null) {
inModel.setChosenDate(DateFormat.yMMMMd("en_US").format(picked.toLocal()));
return "${picked.year},${picked.month},${picked.day}";
}
}
No comments:
Post a Comment