Journal section added
This commit is contained in:
@@ -1,11 +1,13 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:sticky_headers/sticky_headers/widget.dart';
|
import 'package:sticky_headers/sticky_headers/widget.dart';
|
||||||
import 'main.dart' as Main;
|
import 'main.dart' as Main;
|
||||||
import 'newActivity.dart';
|
import 'newActivity.dart';
|
||||||
import 'Data.dart';
|
import 'Data.dart';
|
||||||
import 'User.dart' as User;
|
import 'User.dart' as User;
|
||||||
|
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||||
class Activities extends StatefulWidget {
|
class Activities extends StatefulWidget {
|
||||||
const Activities({Key? key}) : super(key: key);
|
const Activities({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@@ -66,7 +68,7 @@ class _ActivitiesState extends State<Activities> {
|
|||||||
// progressDialog.update(value: 100);
|
// progressDialog.update(value: 100);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
Map<Widget, List<Widget>> activitiesGroups =<Widget, List<Widget>>{};
|
Map<String, ActivityListDateGroup> activitiesGroups = <String, ActivityListDateGroup>{};
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// progressDialog = ProgressDialog(context: context);
|
// progressDialog = ProgressDialog(context: context);
|
||||||
@@ -172,14 +174,15 @@ class _ActivitiesState extends State<Activities> {
|
|||||||
drawer: Main.navDrawer(context, 2),
|
drawer: Main.navDrawer(context, 2),
|
||||||
body: Container(
|
body: Container(
|
||||||
padding: EdgeInsets.all(0),
|
padding: EdgeInsets.all(0),
|
||||||
child: ListView.builder(
|
child: ScrollablePositionedList.builder(
|
||||||
|
itemScrollController: scrollController,
|
||||||
itemCount: activitiesGroups.length,
|
itemCount: activitiesGroups.length,
|
||||||
itemBuilder: (context, index){
|
itemBuilder: (context, index){
|
||||||
// return activities[index];
|
// return activities[index];
|
||||||
return StickyHeader(
|
return StickyHeader(
|
||||||
|
|
||||||
header:activitiesGroups.keys.toList()[index],
|
header:activitiesGroups.values.toList()[index].dateSeperator,
|
||||||
content:Column(children: activitiesGroups.values.toList()[index],)
|
content:Column(children: activitiesGroups.values.toList()[index].activities,)
|
||||||
);
|
);
|
||||||
|
|
||||||
})
|
})
|
||||||
@@ -189,13 +192,17 @@ class _ActivitiesState extends State<Activities> {
|
|||||||
// ))
|
// ))
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
DateFormat dFormat = DateFormat("yyyy-MM-dd");
|
||||||
|
ItemScrollController scrollController = ItemScrollController();
|
||||||
|
ScrollController controller = ScrollController();
|
||||||
int searchTime = 0;
|
int searchTime = 0;
|
||||||
Map<Widget,List<Widget>> PrintTasks() {
|
Map<String,ActivityListDateGroup> PrintTasks() {
|
||||||
Map<Widget,List<Widget>> _tasks = <Widget,List<Widget>>{};
|
|
||||||
|
Map<String,ActivityListDateGroup> _tasks = <String,ActivityListDateGroup>{};
|
||||||
|
|
||||||
print('Priting cats : ' + User.taskTypes.length.toString());
|
print('Priting cats : ' + User.taskTypes.length.toString());
|
||||||
String lastDate = "";
|
String lastDate = "";
|
||||||
DateFormat dFormat = DateFormat("MM/dd");
|
|
||||||
Map<String, int> productivtyActs = <String, int>{};
|
Map<String, int> productivtyActs = <String, int>{};
|
||||||
Map<String, int> unproductivtyActs = <String, int>{};
|
Map<String, int> unproductivtyActs = <String, int>{};
|
||||||
Map<String, int> totalMinutes = <String, int>{};
|
Map<String, int> totalMinutes = <String, int>{};
|
||||||
@@ -248,7 +255,8 @@ class _ActivitiesState extends State<Activities> {
|
|||||||
int prodActs = productivtyActs[thisDate] ?? 0;
|
int prodActs = productivtyActs[thisDate] ?? 0;
|
||||||
int unProdActs = unproductivtyActs[thisDate] ?? 0;
|
int unProdActs = unproductivtyActs[thisDate] ?? 0;
|
||||||
//_tasks.add(DateSeperator(thisDate, prodActs, unProdActs));
|
//_tasks.add(DateSeperator(thisDate, prodActs, unProdActs));
|
||||||
_tasks.putIfAbsent(DateSeperator(thisDate, prodActs, unProdActs), () => [Container()]);
|
// _tasks.putIfAbsent(DateSeperator(thisDate, prodActs, unProdActs), () => [List.generate(0, (i) => List(2));]);
|
||||||
|
_tasks.putIfAbsent(thisDate, () => ActivityListDateGroup(DateSeperator(thisDate, prodActs, unProdActs), []));
|
||||||
print('adding');
|
print('adding');
|
||||||
lastDate = thisDate;
|
lastDate = thisDate;
|
||||||
}
|
}
|
||||||
@@ -260,7 +268,8 @@ class _ActivitiesState extends State<Activities> {
|
|||||||
bool productive = element.taskType.cat?.productive ?? true;
|
bool productive = element.taskType.cat?.productive ?? true;
|
||||||
Widget task = ActivityCard(context, name, element.startTime, element.endTime, productive, color, element, totalMinutes[thisDate] ?? 0);
|
Widget task = ActivityCard(context, name, element.startTime, element.endTime, productive, color, element, totalMinutes[thisDate] ?? 0);
|
||||||
// print('Activity : ${name} ,sTime: ${element.startTime}, eTime: ${element.endTime}');
|
// print('Activity : ${name} ,sTime: ${element.startTime}, eTime: ${element.endTime}');
|
||||||
_tasks.values.toList()[_tasks.length-1].add(task);
|
// _tasks.values.toList()[_tasks.length-1].add(task);
|
||||||
|
_tasks[thisDate]!.activities.add(task);
|
||||||
}
|
}
|
||||||
//Check for gaps
|
//Check for gaps
|
||||||
if(i < User.activities.length-1){
|
if(i < User.activities.length-1){
|
||||||
@@ -268,7 +277,8 @@ class _ActivitiesState extends State<Activities> {
|
|||||||
if(gap > 10) {
|
if(gap > 10) {
|
||||||
Widget addGap = timeGap(User.activities[i].trueStartTime, User.activities[i+1].trueEndTime);
|
Widget addGap = timeGap(User.activities[i].trueStartTime, User.activities[i+1].trueEndTime);
|
||||||
|
|
||||||
_tasks.values.toList()[_tasks.length-1].add(addGap);
|
//_tasks.values.toList()[_tasks.length-1].add(addGap);
|
||||||
|
_tasks[thisDate]!.activities.add(addGap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -320,7 +330,22 @@ class _ActivitiesState extends State<Activities> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget DateSeperator(date, prodActs, unprodActs) {
|
void OnJumpToDate(date){
|
||||||
|
int itemId = 0;
|
||||||
|
List<String> keys = activitiesGroups.keys.toList();
|
||||||
|
List<ActivityListDateGroup> values = activitiesGroups.values.toList();
|
||||||
|
for(int i =0; i < activitiesGroups.length; i++){
|
||||||
|
|
||||||
|
if(keys[i] == dFormat.format(date)){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
itemId++;
|
||||||
|
// itemId+= values[i].activities.length;
|
||||||
|
}
|
||||||
|
scrollController.scrollTo(index: itemId, duration: Duration(seconds: 1),curve: Curves.fastOutSlowIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget DateSeperator(date, prodActs, unprodActs,{Function? onTap}) {
|
||||||
// double prodPercentage = (prodActs / (prodActs + unprodActs)) * 100;
|
// double prodPercentage = (prodActs / (prodActs + unprodActs)) * 100;
|
||||||
|
|
||||||
return FutureBuilder(
|
return FutureBuilder(
|
||||||
@@ -337,18 +362,29 @@ class _ActivitiesState extends State<Activities> {
|
|||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
InkWell(
|
||||||
children: [
|
onTap: (){
|
||||||
SizedBox(width: 15,),
|
DatePicker.showDatePicker(context,
|
||||||
Icon(Icons.circle),
|
showTitleActions: true,
|
||||||
SizedBox(
|
minTime: User.activities[User.activities.length-1].startTime,
|
||||||
width: 10,
|
maxTime: User.activities[0].startTime,
|
||||||
),
|
theme: DatePickerTheme(), onChanged: (date) {
|
||||||
Text(
|
// print('change $date');
|
||||||
date,
|
}, onConfirm: OnJumpToDate, currentTime: DateTime.parse(date), locale: LocaleType.en);
|
||||||
style: TextStyle(fontSize: 18),
|
},
|
||||||
),
|
child: Row(
|
||||||
],
|
children: [
|
||||||
|
SizedBox(width: 15,),
|
||||||
|
Icon(Icons.circle),
|
||||||
|
SizedBox(
|
||||||
|
width: 10,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
date,
|
||||||
|
style: TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -550,5 +586,11 @@ class _ActivitiesState extends State<Activities> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ActivityListDateGroup{
|
||||||
|
ActivityListDateGroup(this.dateSeperator, this.activities);
|
||||||
|
Widget dateSeperator;
|
||||||
|
List<Widget> activities;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
List<Activity> selectedActivities = [];
|
List<Activity> selectedActivities = [];
|
||||||
|
|||||||
@@ -142,6 +142,19 @@ class ProjectStep{
|
|||||||
DateTime? finishedDate;
|
DateTime? finishedDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Journal{
|
||||||
|
|
||||||
|
Journal(this.id, this.day, {this.title, this.description});
|
||||||
|
|
||||||
|
String id;
|
||||||
|
DateTime day;
|
||||||
|
String? title;
|
||||||
|
String? description;
|
||||||
|
|
||||||
|
static String colTitle = 'Title';
|
||||||
|
static String colDescription = 'Desc';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
165
lib/Journal.dart
Normal file
165
lib/Journal.dart
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
import 'package:firebase_auth/firebase_auth.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||||
|
import 'package:tasktracker/NewJournal.dart';
|
||||||
|
import 'package:tasktracker/main.dart';
|
||||||
|
import 'User.dart' as User;
|
||||||
|
import 'Dialogs.dart';
|
||||||
|
|
||||||
|
class JournalPage extends StatefulWidget {
|
||||||
|
const JournalPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<JournalPage> createState() => _JournalPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _JournalPageState extends State<JournalPage> {
|
||||||
|
|
||||||
|
bool selecting = false;
|
||||||
|
List<int> selectedIndexes = [];
|
||||||
|
var refreshStream;
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
// TODO: implement initState
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
refreshStream = User.refreshStream.stream.listen((event) {if(!event){setState(() {
|
||||||
|
|
||||||
|
});}});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
// TODO: implement dispose
|
||||||
|
super.dispose();
|
||||||
|
refreshStream?.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewJournal())).then((val) {
|
||||||
|
setState(() {});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
label: Text("New Entry"),
|
||||||
|
icon: Icon(Icons.add)),
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
FaIcon(FontAwesomeIcons.bookJournalWhills),
|
||||||
|
SizedBox(
|
||||||
|
width: 15,
|
||||||
|
),
|
||||||
|
Text('Journal')
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
if(selecting && selectedIndexes.length > 0)InkWell(onTap:() async{
|
||||||
|
selecting=false;
|
||||||
|
for (int element in selectedIndexes) {
|
||||||
|
await User.UserOperations.deleteJournal(User.journal[element].id);
|
||||||
|
}setState(() {
|
||||||
|
|
||||||
|
});
|
||||||
|
},child: Container(margin:EdgeInsets.all(8),child: Icon(Icons.delete))),
|
||||||
|
if(selecting)InkWell(onTap:(){
|
||||||
|
selecting=false;
|
||||||
|
setState(() {
|
||||||
|
|
||||||
|
});
|
||||||
|
},child: Container(margin:EdgeInsets.all(8),child: Icon(Icons.cancel))),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
drawer: navDrawer(context, 8),
|
||||||
|
body: Container(
|
||||||
|
padding: EdgeInsets.all(8),
|
||||||
|
child: ScrollablePositionedList.builder(
|
||||||
|
itemCount: User.journal.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return Container(
|
||||||
|
|
||||||
|
//duration: Duration(milliseconds: 500),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: (){
|
||||||
|
if(selecting){
|
||||||
|
if(selectedIndexes.contains(index)){
|
||||||
|
selectedIndexes.remove(index);
|
||||||
|
}else{
|
||||||
|
selectedIndexes.add(index);
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLongPress: () {
|
||||||
|
selecting = !selecting;
|
||||||
|
if(!selectedIndexes.contains(index)){selectedIndexes.add(index);}
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
if (selecting)
|
||||||
|
Checkbox(
|
||||||
|
value: selectedIndexes.contains(index),
|
||||||
|
onChanged: (newVal) {
|
||||||
|
if(selectedIndexes.contains(index)){
|
||||||
|
selectedIndexes.remove(index);
|
||||||
|
}else{
|
||||||
|
selectedIndexes.add(index);
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Card(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(User.journal[index].title ?? '', style: TextStyle(fontSize: 18)),
|
||||||
|
Text(DateFormat('yyyy-MM-dd').format(User.journal[index].day))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (User.journal[index].description != null && User.journal[index].description!.isNotEmpty)
|
||||||
|
Text(User.journal[index].description!)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if(selecting)InkWell(onTap:(){
|
||||||
|
Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewJournal(date: User.journal[index].day, title: User.journal[index].title, text: User.journal[index].description,))).then((val) {
|
||||||
|
setState(() {});
|
||||||
|
});
|
||||||
|
selecting=false;
|
||||||
|
setState(() {
|
||||||
|
|
||||||
|
});
|
||||||
|
},child: Container(margin:EdgeInsets.all(8),child: FaIcon(FontAwesomeIcons.edit)))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
189
lib/NewJournal.dart
Normal file
189
lib/NewJournal.dart
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
|
||||||
|
import 'User.dart' as User;
|
||||||
|
import 'Data.dart';
|
||||||
|
|
||||||
|
class NewJournal extends StatefulWidget {
|
||||||
|
NewJournal({Key? key, this.title, this.text, this.date}) : super(key: key);
|
||||||
|
late String? title;
|
||||||
|
late String? text;
|
||||||
|
late DateTime? date;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_NewJournalState createState() => _NewJournalState(title: title, text: text, m_date: date);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NewJournalState extends State<NewJournal> {
|
||||||
|
bool editing =false;
|
||||||
|
_NewJournalState({String? title, String? text, DateTime? m_date}){
|
||||||
|
if(m_date!= null){
|
||||||
|
editing = true;
|
||||||
|
date=m_date;
|
||||||
|
oldDate =m_date;
|
||||||
|
}
|
||||||
|
|
||||||
|
titleController.text = title??'';
|
||||||
|
descriptionEditingController.text = text??'';
|
||||||
|
}
|
||||||
|
|
||||||
|
TextEditingController titleController = TextEditingController();
|
||||||
|
TextEditingController descriptionEditingController = TextEditingController();
|
||||||
|
bool productive = true;
|
||||||
|
Color pickerColor = Colors.blue;
|
||||||
|
DateTime date= DateTime.now();
|
||||||
|
DateTime oldDate = DateTime.now();
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(title: Text('${(editing) ? 'Edit' : 'New'} Journal Entry')),
|
||||||
|
body: Container(
|
||||||
|
height: MediaQuery.of(context).size.height,
|
||||||
|
child: Column(mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
|
||||||
|
Expanded(flex: 9,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(20, 50, 20, 50),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
InkWell(
|
||||||
|
onTap: (){
|
||||||
|
DatePicker.showDatePicker(context,
|
||||||
|
showTitleActions: true,
|
||||||
|
minTime: User.activities[User.activities.length-1].startTime,
|
||||||
|
maxTime: User.activities[0].startTime,
|
||||||
|
theme: DatePickerTheme(), onChanged: (date) {
|
||||||
|
// print('change $date');
|
||||||
|
}, onConfirm: (newDate){
|
||||||
|
date = newDate;
|
||||||
|
}, currentTime: DateTime.now(), locale: LocaleType.en);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.symmetric(vertical: 10),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
FaIcon(FontAwesomeIcons.calendarDay),
|
||||||
|
SizedBox(width: 10,),
|
||||||
|
Text('Date : '),
|
||||||
|
SizedBox(width: 30,),
|
||||||
|
Text(
|
||||||
|
DateFormat('yyyy-MM-dd').format(date),
|
||||||
|
style: TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
SizedBox(width: 30,),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 10,),
|
||||||
|
Column(children: [
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.all(10),
|
||||||
|
child: TextField(
|
||||||
|
controller: titleController,
|
||||||
|
decoration: InputDecoration(hintText: 'Title (something special happened this day)', border: OutlineInputBorder()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.all(10),
|
||||||
|
child: TextField(
|
||||||
|
|
||||||
|
maxLines: 25,
|
||||||
|
minLines: 10,
|
||||||
|
controller: descriptionEditingController,
|
||||||
|
decoration: InputDecoration(hintText: 'So how was your day? Write it all down here!', border: OutlineInputBorder()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
))),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 20),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 5,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 0),
|
||||||
|
child: ElevatedButton(
|
||||||
|
style: ElevatedButton.styleFrom(primary: Colors.red, shape: StadiumBorder()),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
Navigator.pop(context);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Text('Back', style: TextStyle(fontSize: 20))))),
|
||||||
|
Expanded(
|
||||||
|
flex: 6,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 0),
|
||||||
|
child: ElevatedButton(
|
||||||
|
style: ElevatedButton.styleFrom(primary: Colors.green, shape: StadiumBorder()),
|
||||||
|
onPressed: () {
|
||||||
|
add_action();
|
||||||
|
},
|
||||||
|
child: Text('${editing ? 'Edit ' : 'Add '}Entry', style: TextStyle(fontSize: 20))))),
|
||||||
|
],
|
||||||
|
))
|
||||||
|
])));
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_action() async {
|
||||||
|
String title = titleController.value.text;
|
||||||
|
String text = descriptionEditingController.text;
|
||||||
|
|
||||||
|
if (title.isEmpty && text.isEmpty) {
|
||||||
|
showAlertDialog(context, 'Empty data', 'Journal entry is empty, Cannot add empty entries');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(editing){
|
||||||
|
await User.UserOperations.editJournal(oldDate,date,title,text);
|
||||||
|
}else{
|
||||||
|
await User.UserOperations.addJournal(date, title, text);
|
||||||
|
}
|
||||||
|
Navigator.of(context).popUntil((route) {
|
||||||
|
return route.isFirst;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _printDuration(Duration duration) {
|
||||||
|
String twoDigits(int n) => n.toString().padLeft(2, "0");
|
||||||
|
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
|
||||||
|
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
|
||||||
|
return "${twoDigits(duration.inHours)}:$twoDigitMinutes:$twoDigitSeconds";
|
||||||
|
}
|
||||||
|
|
||||||
|
showAlertDialog(BuildContext context, String title, String message) {
|
||||||
|
// set up the button
|
||||||
|
Widget okButton = TextButton(
|
||||||
|
child: Text("OK"),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// set up the AlertDialog
|
||||||
|
AlertDialog alert = AlertDialog(
|
||||||
|
title: Text(title),
|
||||||
|
content: Text(message),
|
||||||
|
actions: [
|
||||||
|
okButton,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// show the dialog
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return alert;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
220
lib/User.dart
220
lib/User.dart
@@ -28,6 +28,7 @@ List<Category> categories = [];
|
|||||||
List<TaskType> taskTypes = [];
|
List<TaskType> taskTypes = [];
|
||||||
List<Activity> activities = [];
|
List<Activity> activities = [];
|
||||||
List<Project> projects = [];
|
List<Project> projects = [];
|
||||||
|
List<Journal> journal = [];
|
||||||
|
|
||||||
bool offline = true;
|
bool offline = true;
|
||||||
bool registered = false;
|
bool registered = false;
|
||||||
@@ -136,14 +137,17 @@ Future<void> refreshUserData({bool forceOffline = false}) async {
|
|||||||
taskTypes = await GetTaskTypes(true);
|
taskTypes = await GetTaskTypes(true);
|
||||||
activities = await GetActivities(true);
|
activities = await GetActivities(true);
|
||||||
projects= await GetProjects(true);
|
projects= await GetProjects(true);
|
||||||
|
journal = await GetJournals(true);
|
||||||
|
|
||||||
refreshStream.add(false);
|
refreshStream.add(false);
|
||||||
}else{
|
}else{
|
||||||
|
|
||||||
await updateCatsList();
|
categories = await GetCategories(false);
|
||||||
await updateTasksList();
|
taskTypes = await GetTaskTypes(false);
|
||||||
await updateActList();
|
activities = await GetActivities(false);
|
||||||
await updateProjectsList();
|
projects= await GetProjects(false);
|
||||||
|
journal = await GetJournals(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_refreshing=false;
|
m_refreshing=false;
|
||||||
@@ -213,6 +217,8 @@ void onCacheDatabaseCreate(Database db, int newVersion) async {
|
|||||||
'CREATE TABLE Projects(id TEXT PRIMARY KEY, ${Project.colName} TEXT, ${Project.colCat} TEXT, ${Project.colSteps} TEXT, ${Project.colEta} INT, ${Project.colDeadline} DATETIME)';
|
'CREATE TABLE Projects(id TEXT PRIMARY KEY, ${Project.colName} TEXT, ${Project.colCat} TEXT, ${Project.colSteps} TEXT, ${Project.colEta} INT, ${Project.colDeadline} DATETIME)';
|
||||||
await db.execute(ProjectsTableSQL);
|
await db.execute(ProjectsTableSQL);
|
||||||
|
|
||||||
|
String JournalTableSQL = 'CREATE TABLE Journal(id TEXT PRIMARY KEY, ${Journal.colTitle} TEXT, ${Journal.colDescription})';
|
||||||
|
await db.execute(JournalTableSQL);
|
||||||
String QueriesTableSQL = 'CREATE TABLE Queries(id INTEGER PRIMARY KEY AUTOINCREMENT, ${Queries.colLink} TEXT,${Queries.colData} TEXT)';
|
String QueriesTableSQL = 'CREATE TABLE Queries(id INTEGER PRIMARY KEY AUTOINCREMENT, ${Queries.colLink} TEXT,${Queries.colData} TEXT)';
|
||||||
// print(QueriesTableSQL);
|
// print(QueriesTableSQL);
|
||||||
await db.execute(QueriesTableSQL);
|
await db.execute(QueriesTableSQL);
|
||||||
@@ -750,6 +756,92 @@ Future<void> UpdateProjectsFromServer() async {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<Journal>> GetJournals(bool forceOffline) async {
|
||||||
|
if (cacheEnabled) {
|
||||||
|
List<Journal> _journals = [];
|
||||||
|
if (offline || forceOffline) {
|
||||||
|
//Retreive from cacheDB
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//Check if server got updated, If not go for cache
|
||||||
|
|
||||||
|
//Validate device_id to check updates
|
||||||
|
|
||||||
|
bool catsUpdated = false;
|
||||||
|
// try {
|
||||||
|
// http.Response update_response = (await http.post(Uri.parse('http://161.97.127.136/task_tracker/check_update.php'), body: <String, String>{"username": username, "device_id": android_id}));
|
||||||
|
// final data = update_response.body.split(',');
|
||||||
|
// catsUpdated = data[0] == '1';
|
||||||
|
// } catch (e) {
|
||||||
|
// print(e);
|
||||||
|
// }
|
||||||
|
|
||||||
|
//Update CacheDB
|
||||||
|
if (!catsUpdated) {
|
||||||
|
await UpdateJournalsFromServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Map> cats = await cacheDb.query('Journal');
|
||||||
|
print(cats.length);
|
||||||
|
for (Map element in cats) {
|
||||||
|
String? id = element['id'].toString();
|
||||||
|
String? title = element[Journal.colTitle].toString();
|
||||||
|
String? text = element[Journal.colDescription].toString();
|
||||||
|
if (id == null || title == null || text == null) {
|
||||||
|
print("Something is null!");
|
||||||
|
print("id:{$id}, title:{$title}, text:${text}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
DateTime day = DateTime.parse(id.replaceAll(username, ''));
|
||||||
|
// print("name:{$catName}, color:{$catColor}, prod:{$catProductive}");
|
||||||
|
_journals.add(Journal(id,day,title: title,description: text));
|
||||||
|
}
|
||||||
|
journal = _journals;
|
||||||
|
} else {
|
||||||
|
print("NC: Updating Categories as $username");
|
||||||
|
try {
|
||||||
|
http.Response response = (await http.post(Uri.parse('http://161.97.127.136/task_tracker/get_journals.php'),
|
||||||
|
body: <String, String>{"username": username, "device_id": await Settings.UUID()}));
|
||||||
|
|
||||||
|
print(response.body);
|
||||||
|
List<String> data = response.body.split("<td>");
|
||||||
|
List<Journal> _categories = [];
|
||||||
|
for (var value in data) {
|
||||||
|
Map<String, dynamic> cat = jsonDecode(value);
|
||||||
|
//print(catData);
|
||||||
|
_categories.add(Journal(cat['id'],DateTime.parse(cat['id'].toString().replaceAll(username, '')), title:cat['title'], description:cat['text']));
|
||||||
|
}
|
||||||
|
journal = _categories;
|
||||||
|
} catch (e) {
|
||||||
|
print("Error while cats NC: $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
journal.sort((a,b)=> b.day.compareTo(a.day));
|
||||||
|
return journal;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> UpdateJournalsFromServer() async {
|
||||||
|
print("Updating Journal as $username");
|
||||||
|
try {
|
||||||
|
http.Response response = (await http.post(Uri.parse('http://161.97.127.136/task_tracker/get_journals.php'),
|
||||||
|
body: <String, String>{"username": username, "device_id": await Settings.UUID()}));
|
||||||
|
|
||||||
|
print(response.body);
|
||||||
|
List<String> data = response.body.split("<td>");
|
||||||
|
await cacheDb.delete("Journal");
|
||||||
|
for (var value in data) {
|
||||||
|
Map<String, dynamic> cat = jsonDecode(value);
|
||||||
|
//print(catData);
|
||||||
|
await cacheDb
|
||||||
|
.rawInsert("INSERT OR REPLACE INTO Journal (id, ${Journal.colTitle},${Journal.colDescription}) "
|
||||||
|
"VALUES ('${cat['id']}','${cat['title'].toString().replaceAll("'", "''")}','${cat['description'].toString().replaceAll("'", "''")}') ");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print("Error while cats $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<TaskType?> getTaskFromId(String taskId) async {
|
Future<TaskType?> getTaskFromId(String taskId) async {
|
||||||
// await GetTaskTypes(false);
|
// await GetTaskTypes(false);
|
||||||
TaskType? cat = null;
|
TaskType? cat = null;
|
||||||
@@ -1221,6 +1313,89 @@ class UserOperations {
|
|||||||
await executeQueries();
|
await executeQueries();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<void> addJournal(DateTime day, String title, String text) async {
|
||||||
|
String id = username + DateFormat('yyyy-MM-dd').format(day);
|
||||||
|
Map<String, String> queryBody = <String, String>{
|
||||||
|
'username': username,
|
||||||
|
'id': id,
|
||||||
|
'title': title,
|
||||||
|
'description': text
|
||||||
|
};
|
||||||
|
if (cacheEnabled) {
|
||||||
|
//Add Query
|
||||||
|
Map<String, Object> query = {Queries.colLink: 'add_journal', Queries.colData: jsonEncode(queryBody)};
|
||||||
|
|
||||||
|
print("adding new query ${query[Queries.colLink]} : ${jsonEncode(queryBody)}");
|
||||||
|
|
||||||
|
await cacheDb.insert('Queries', query);
|
||||||
|
|
||||||
|
//update Cache
|
||||||
|
Map<String, Object> data = {
|
||||||
|
'id':id,
|
||||||
|
Journal.colTitle: title,
|
||||||
|
Journal.colDescription:text
|
||||||
|
};
|
||||||
|
await cacheDb.insert('Journal', data);
|
||||||
|
await refreshUserData(forceOffline: true);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
http.Response queryResponse = (await http.post(Uri.parse('http://161.97.127.136/task_tracker/add_journal.php'), body: queryBody));
|
||||||
|
print("Query executed : Results{${queryResponse.body}");
|
||||||
|
if (queryResponse.body.toLowerCase().contains("success")) {
|
||||||
|
//Success
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('NC: Error adding journal entry $e}');
|
||||||
|
}
|
||||||
|
//executeQueries();
|
||||||
|
}
|
||||||
|
//Add to server and refresh Cache
|
||||||
|
await executeQueries();
|
||||||
|
|
||||||
|
}
|
||||||
|
static Future<void> editJournal(DateTime oldDay, DateTime day, String title, String text) async {
|
||||||
|
String oldId = username + DateFormat('yyyy-MM-dd').format(oldDay);
|
||||||
|
String id = username + DateFormat('yyyy-MM-dd').format(day);
|
||||||
|
Map<String, String> queryBody = <String, String>{
|
||||||
|
'username': username,
|
||||||
|
'old_id':oldId,
|
||||||
|
'id': id,
|
||||||
|
'title': title,
|
||||||
|
'description': text
|
||||||
|
};
|
||||||
|
if (cacheEnabled) {
|
||||||
|
//Add Query
|
||||||
|
Map<String, Object> query = {Queries.colLink: 'edit_journal', Queries.colData: jsonEncode(queryBody)};
|
||||||
|
|
||||||
|
print("adding new query ${query[Queries.colLink]} : ${jsonEncode(queryBody)}");
|
||||||
|
|
||||||
|
await cacheDb.insert('Queries', query);
|
||||||
|
await cacheDb.rawUpdate("UPDATE Journal SET id='$id', ${Journal.colTitle}='${title.toString().replaceAll("'", "''")}', ${Journal.colDescription}='${text.toString().replaceAll("'", "''")}' WHERE id='$oldId'");
|
||||||
|
//update Cache
|
||||||
|
Map<String, Object> data = {
|
||||||
|
'id':id,
|
||||||
|
Journal.colTitle: title,
|
||||||
|
Journal.colDescription:text
|
||||||
|
};
|
||||||
|
// await cacheDb.insert('Journal', data);
|
||||||
|
await refreshUserData(forceOffline: true);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
http.Response queryResponse = (await http.post(Uri.parse('http://161.97.127.136/task_tracker/edit_journal.php'), body: queryBody));
|
||||||
|
print("Query executed : Results{${queryResponse.body}");
|
||||||
|
if (queryResponse.body.toLowerCase().contains("success")) {
|
||||||
|
//Success
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('NC: Error adding journal entry $e}');
|
||||||
|
}
|
||||||
|
//executeQueries();
|
||||||
|
}
|
||||||
|
//Add to server and refresh Cache
|
||||||
|
await executeQueries();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
static Future<void> deleteTask(String name, {bulk = false}) async {
|
static Future<void> deleteTask(String name, {bulk = false}) async {
|
||||||
Map<String, String> queryBody = <String, String>{
|
Map<String, String> queryBody = <String, String>{
|
||||||
'id': username + name,
|
'id': username + name,
|
||||||
@@ -1372,6 +1547,43 @@ class UserOperations {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<void> deleteJournal(String id, {bulk = false}) async {
|
||||||
|
Map<String, String> queryBody = <String, String>{
|
||||||
|
'username': username,
|
||||||
|
'id': id,
|
||||||
|
};
|
||||||
|
//Add Query
|
||||||
|
Map<String, Object> query = {Queries.colLink: 'delete_journal', Queries.colData: jsonEncode(queryBody)};
|
||||||
|
|
||||||
|
print("adding new query ${query[Queries.colLink]} : ${jsonEncode(queryBody)}");
|
||||||
|
|
||||||
|
if (cacheEnabled) {
|
||||||
|
await cacheDb.insert('Queries', query);
|
||||||
|
|
||||||
|
//update Cache
|
||||||
|
String deleteQuery =
|
||||||
|
"DELETE FROM Journal WHERE id='$id'";
|
||||||
|
print("delteQuery : $deleteQuery");
|
||||||
|
|
||||||
|
await cacheDb.rawDelete(deleteQuery);
|
||||||
|
await refreshUserData(forceOffline: true);
|
||||||
|
//Add to server and refresh Cache
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
http.Response queryResponse = (await http.post(Uri.parse('http://161.97.127.136/task_tracker/delete_journal.php'), body: queryBody));
|
||||||
|
print("Query executed : Results{${queryResponse.body}");
|
||||||
|
if (queryResponse.body.toLowerCase().contains("success")) {
|
||||||
|
//Success
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('NC: Error deleting journal $e}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!bulk) {
|
||||||
|
await executeQueries();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Future<void> executeQueries() async {
|
static Future<void> executeQueries() async {
|
||||||
if (cacheEnabled) {
|
if (cacheEnabled) {
|
||||||
if (offline) {
|
if (offline) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import 'package:intl/intl.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:tasktracker/Categories.dart';
|
import 'package:tasktracker/Categories.dart';
|
||||||
|
import 'package:tasktracker/Journal.dart';
|
||||||
import 'package:tasktracker/Projects.dart';
|
import 'package:tasktracker/Projects.dart';
|
||||||
import 'package:tasktracker/Todo.dart';
|
import 'package:tasktracker/Todo.dart';
|
||||||
import 'package:tasktracker/Welcome.dart';
|
import 'package:tasktracker/Welcome.dart';
|
||||||
@@ -111,7 +112,8 @@ class MyApp extends StatelessWidget {
|
|||||||
'/Categories': (context) => const Categories(),
|
'/Categories': (context) => const Categories(),
|
||||||
'/Activities': (context) => const Activities(),
|
'/Activities': (context) => const Activities(),
|
||||||
'/Settings': (context) => const SettingsPage(),
|
'/Settings': (context) => const SettingsPage(),
|
||||||
'/Projects':(context)=> const Projects()
|
'/Projects':(context)=> const Projects(),
|
||||||
|
'/Journal': (context)=> const JournalPage()
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -283,7 +285,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||||||
ongoingActName = "";
|
ongoingActName = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mounted) {
|
if(this.mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -1003,7 +1005,7 @@ Drawer navDrawer(BuildContext context, int pageIndex) {
|
|||||||
ListTile(
|
ListTile(
|
||||||
selected: (pageIndex == 7),
|
selected: (pageIndex == 7),
|
||||||
title: Text('Projects'),
|
title: Text('Projects'),
|
||||||
leading: Icon(Icons.work_outline_sharp, color: Theme.of(context).primaryColor),
|
leading: FaIcon(FontAwesomeIcons.rocket, color: Theme.of(context).primaryColor),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (pageIndex == 7) {
|
if (pageIndex == 7) {
|
||||||
return;
|
return;
|
||||||
@@ -1011,6 +1013,17 @@ Drawer navDrawer(BuildContext context, int pageIndex) {
|
|||||||
Navigator.of(context).pushReplacementNamed('/Projects');
|
Navigator.of(context).pushReplacementNamed('/Projects');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
selected: (pageIndex == 8),
|
||||||
|
title: Text('Journal'),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.bookJournalWhills, color: Theme.of(context).primaryColor),
|
||||||
|
onTap: () {
|
||||||
|
if (pageIndex == 8) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Navigator.of(context).pushReplacementNamed('/Journal');
|
||||||
|
},
|
||||||
|
),
|
||||||
// ListTile(
|
// ListTile(
|
||||||
// selected: (pageIndex == 7),
|
// selected: (pageIndex == 7),
|
||||||
// title: Text('TODO'),
|
// title: Text('TODO'),
|
||||||
@@ -1046,37 +1059,6 @@ Drawer navDrawer(BuildContext context, int pageIndex) {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyPlayerBar extends CustomPainter {
|
|
||||||
MyPlayerBar(this.max, this.value, {this.background = Colors.lightBlue, this.fill = Colors.blue});
|
|
||||||
|
|
||||||
Color background = Colors.lightBlue;
|
|
||||||
Color fill = Colors.blue;
|
|
||||||
final int max;
|
|
||||||
final int value;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void paint(Canvas canvas, Size size) {
|
|
||||||
Paint paint = Paint();
|
|
||||||
double cursor = (value * size.width) / max;
|
|
||||||
Radius cornerRadius = Radius.circular(10.0);
|
|
||||||
|
|
||||||
// Already played half color (your darker orange)
|
|
||||||
paint.color = background;
|
|
||||||
|
|
||||||
// Painting already played half
|
|
||||||
canvas.drawRRect(RRect.fromRectAndCorners(Rect.fromLTWH(0.0, 0.0, cursor, size.height), topLeft: cornerRadius, bottomLeft: cornerRadius), paint);
|
|
||||||
|
|
||||||
// Yet to play half color (your lighter orange)
|
|
||||||
paint.color = fill;
|
|
||||||
|
|
||||||
// Painting the remaining space
|
|
||||||
canvas.drawRRect(RRect.fromRectAndCorners(Rect.fromLTWH(cursor, 0.0, size.width - cursor, size.height), bottomRight: cornerRadius, topRight: cornerRadius), paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool shouldRepaint(CustomPainter oldDelegate) => true;
|
|
||||||
}
|
|
||||||
|
|
||||||
class CatMapData {
|
class CatMapData {
|
||||||
CatMapData(this.name, this.time, this.color);
|
CatMapData(this.name, this.time, this.color);
|
||||||
final String name;
|
final String name;
|
||||||
|
|||||||
@@ -450,6 +450,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.27.3"
|
version: "0.27.3"
|
||||||
|
scrollable_positioned_list:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: scrollable_positioned_list
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.3"
|
||||||
shared_preferences:
|
shared_preferences:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
||||||
|
scrollable_positioned_list: ^0.2.3
|
||||||
sticky_headers: ^0.2.0
|
sticky_headers: ^0.2.0
|
||||||
flutter_switch: ^0.3.2
|
flutter_switch: ^0.3.2
|
||||||
sqflite_common_ffi: ^2.1.0+2
|
sqflite_common_ffi: ^2.1.0+2
|
||||||
|
|||||||
Reference in New Issue
Block a user