parent
3c52931538
commit
2e04969689
|
@ -0,0 +1,39 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class AddDialog extends StatelessWidget {
|
||||
final ValueChanged<String> onAdd;
|
||||
final InputDecoration decoration;
|
||||
|
||||
const AddDialog({Key key, this.onAdd, this.decoration}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var textController = TextEditingController();
|
||||
return new AlertDialog(
|
||||
contentPadding: const EdgeInsets.all(16.0),
|
||||
content: new Row(children: <Widget>[
|
||||
Expanded(
|
||||
child: new TextField(
|
||||
autofocus: true,
|
||||
decoration: this.decoration,
|
||||
controller: textController,
|
||||
),
|
||||
)
|
||||
]),
|
||||
actions: <Widget>[
|
||||
new FlatButton(
|
||||
child: const Text('CANCEL'),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
new FlatButton(
|
||||
child: const Text('ADD'),
|
||||
onPressed: () {
|
||||
if (this.onAdd != null && textController.text.isNotEmpty)
|
||||
this.onAdd(textController.text);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:vikunja_app/global.dart';
|
||||
import 'package:vikunja_app/models/task.dart';
|
||||
|
||||
class TaskTile extends StatefulWidget {
|
||||
final Task task;
|
||||
final VoidCallback onEdit;
|
||||
final bool loading;
|
||||
|
||||
const TaskTile(
|
||||
{Key key, @required this.task, this.onEdit, this.loading = false})
|
||||
: assert(task != null),
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
TaskTileState createState() {
|
||||
return new TaskTileState(this.task, this.loading);
|
||||
}
|
||||
}
|
||||
|
||||
class TaskTileState extends State<TaskTile> {
|
||||
bool _loading;
|
||||
Task _currentTask;
|
||||
|
||||
TaskTileState(this._currentTask, this._loading)
|
||||
: assert(_currentTask != null),
|
||||
assert(_loading != null);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_loading) {
|
||||
return ListTile(
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: SizedBox(
|
||||
height: Checkbox.width,
|
||||
width: Checkbox.width,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2.0,
|
||||
)),
|
||||
),
|
||||
title: Text(_currentTask.text),
|
||||
subtitle:
|
||||
_currentTask.description == null || _currentTask.description.isEmpty
|
||||
? null
|
||||
: Text(_currentTask.description),
|
||||
trailing: IconButton(icon: Icon(Icons.settings), onPressed: null),
|
||||
);
|
||||
}
|
||||
return CheckboxListTile(
|
||||
title: Text(_currentTask.text),
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
value: _currentTask.done ?? false,
|
||||
subtitle:
|
||||
_currentTask.description == null || _currentTask.description.isEmpty
|
||||
? null
|
||||
: Text(_currentTask.description),
|
||||
secondary:
|
||||
IconButton(icon: Icon(Icons.settings), onPressed: widget.onEdit),
|
||||
onChanged: _change,
|
||||
);
|
||||
}
|
||||
|
||||
void _change(bool value) async {
|
||||
setState(() {
|
||||
this._loading = true;
|
||||
});
|
||||
Task newTask = await _updateTask(_currentTask, value);
|
||||
setState(() {
|
||||
this._currentTask = newTask;
|
||||
this._loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
Future<Task> _updateTask(Task task, bool checked) {
|
||||
// TODO use copyFrom
|
||||
return VikunjaGlobal.of(context).taskService.update(Task(
|
||||
id: task.id,
|
||||
done: checked,
|
||||
text: task.text,
|
||||
description: task.description,
|
||||
owner: null,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
typedef Future<void> TaskChanged(Task task, bool newValue);
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:vikunja_app/components/AddDialog.dart';
|
||||
import 'package:vikunja_app/global.dart';
|
||||
import 'package:vikunja_app/models/namespace.dart';
|
||||
import 'package:vikunja_app/models/task.dart';
|
||||
|
@ -18,35 +19,41 @@ class NamespaceFragment extends StatefulWidget {
|
|||
|
||||
class _NamespaceFragmentState extends State<NamespaceFragment> {
|
||||
List<TaskList> _lists = [];
|
||||
bool _loading = true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: new ListView(
|
||||
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||
children: ListTile.divideTiles(
|
||||
context: context,
|
||||
tiles: _lists.map((ls) => Dismissible(
|
||||
key: Key(ls.id.toString()),
|
||||
direction: DismissDirection.startToEnd,
|
||||
child: ListTile(
|
||||
title: new Text(ls.title),
|
||||
onTap: () => _openList(context, ls),
|
||||
trailing: Icon(Icons.arrow_right),
|
||||
),
|
||||
background: Container(
|
||||
color: Colors.red,
|
||||
child: const ListTile(
|
||||
leading: Icon(Icons.delete,
|
||||
color: Colors.white, size: 36.0)),
|
||||
),
|
||||
onDismissed: (direction) {
|
||||
_removeList(ls).then((_) => Scaffold.of(context)
|
||||
.showSnackBar(
|
||||
SnackBar(content: Text("${ls.title} removed"))));
|
||||
},
|
||||
))).toList(),
|
||||
),
|
||||
body: !this._loading
|
||||
? RefreshIndicator(
|
||||
child: new ListView(
|
||||
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||
children: ListTile.divideTiles(
|
||||
context: context,
|
||||
tiles: _lists.map((ls) => Dismissible(
|
||||
key: Key(ls.id.toString()),
|
||||
direction: DismissDirection.startToEnd,
|
||||
child: ListTile(
|
||||
title: new Text(ls.title),
|
||||
onTap: () => _openList(context, ls),
|
||||
trailing: Icon(Icons.arrow_right),
|
||||
),
|
||||
background: Container(
|
||||
color: Colors.red,
|
||||
child: const ListTile(
|
||||
leading: Icon(Icons.delete,
|
||||
color: Colors.white, size: 36.0)),
|
||||
),
|
||||
onDismissed: (direction) {
|
||||
_removeList(ls).then((_) => Scaffold.of(context)
|
||||
.showSnackBar(SnackBar(
|
||||
content: Text("${ls.title} removed"))));
|
||||
},
|
||||
))).toList(),
|
||||
),
|
||||
onRefresh: _updateLists,
|
||||
)
|
||||
: Center(child: CircularProgressIndicator()),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () => _addListDialog(), child: const Icon(Icons.add)),
|
||||
);
|
||||
|
@ -65,11 +72,14 @@ class _NamespaceFragmentState extends State<NamespaceFragment> {
|
|||
.then((_) => _updateLists());
|
||||
}
|
||||
|
||||
_updateLists() {
|
||||
VikunjaGlobal.of(context)
|
||||
Future<void> _updateLists() {
|
||||
return VikunjaGlobal.of(context)
|
||||
.listService
|
||||
.getByNamespace(widget.namespace.id)
|
||||
.then((lists) => setState(() => this._lists = lists));
|
||||
.then((lists) => setState(() {
|
||||
this._lists = lists;
|
||||
this._loading = false;
|
||||
}));
|
||||
}
|
||||
|
||||
_openList(BuildContext context, TaskList list) {
|
||||
|
@ -78,37 +88,12 @@ class _NamespaceFragmentState extends State<NamespaceFragment> {
|
|||
}
|
||||
|
||||
_addListDialog() {
|
||||
var textController = new TextEditingController();
|
||||
showDialog(
|
||||
context: context,
|
||||
child: new AlertDialog(
|
||||
contentPadding: const EdgeInsets.all(16.0),
|
||||
content: new Row(children: <Widget>[
|
||||
Expanded(
|
||||
child: new TextField(
|
||||
autofocus: true,
|
||||
decoration: new InputDecoration(
|
||||
labelText: 'List Name', hintText: 'eg. Shopping List'),
|
||||
controller: textController,
|
||||
),
|
||||
)
|
||||
]),
|
||||
actions: <Widget>[
|
||||
new FlatButton(
|
||||
child: const Text('CANCEL'),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
new FlatButton(
|
||||
child: const Text('ADD'),
|
||||
onPressed: () {
|
||||
if (textController.text.isNotEmpty) {
|
||||
_addList(textController.text);
|
||||
}
|
||||
Navigator.pop(context);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
builder: (_) => AddDialog(
|
||||
onAdd: _addList,
|
||||
decoration: new InputDecoration(
|
||||
labelText: 'List Name', hintText: 'eg. Shopping List')),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:vikunja_app/components/AddDialog.dart';
|
||||
import 'package:vikunja_app/components/GravatarImage.dart';
|
||||
import 'package:vikunja_app/fragments/namespace.dart';
|
||||
import 'package:vikunja_app/fragments/placeholder.dart';
|
||||
|
@ -19,6 +22,7 @@ class HomePageState extends State<HomePage> {
|
|||
? _namespaces[_selectedDrawerIndex]
|
||||
: null;
|
||||
int _selectedDrawerIndex = -1;
|
||||
bool _loading = true;
|
||||
|
||||
_getDrawerItemWidget(int pos) {
|
||||
if (pos == -1) {
|
||||
|
@ -33,38 +37,13 @@ class HomePageState extends State<HomePage> {
|
|||
}
|
||||
|
||||
_addNamespaceDialog() {
|
||||
var textController = new TextEditingController();
|
||||
showDialog(
|
||||
context: context,
|
||||
child: new AlertDialog(
|
||||
contentPadding: const EdgeInsets.all(16.0),
|
||||
content: new Row(children: <Widget>[
|
||||
Expanded(
|
||||
child: new TextField(
|
||||
autofocus: true,
|
||||
context: context,
|
||||
builder: (_) => AddDialog(
|
||||
onAdd: _addNamespace,
|
||||
decoration: new InputDecoration(
|
||||
labelText: 'Namespace', hintText: 'eg. Family Namespace'),
|
||||
controller: textController,
|
||||
),
|
||||
)
|
||||
]),
|
||||
actions: <Widget>[
|
||||
new FlatButton(
|
||||
child: const Text('CANCEL'),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
new FlatButton(
|
||||
child: const Text('ADD'),
|
||||
onPressed: () {
|
||||
if (textController.text.isNotEmpty) {
|
||||
_addNamespace(textController.text);
|
||||
}
|
||||
Navigator.pop(context);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
labelText: 'Namespace', hintText: 'eg. Personal Namespace'),
|
||||
));
|
||||
}
|
||||
|
||||
_addNamespace(String name) {
|
||||
|
@ -74,9 +53,10 @@ class HomePageState extends State<HomePage> {
|
|||
.then((_) => _updateNamespaces());
|
||||
}
|
||||
|
||||
_updateNamespaces() {
|
||||
VikunjaGlobal.of(context).namespaceService.getAll().then((result) {
|
||||
Future<void> _updateNamespaces() {
|
||||
return VikunjaGlobal.of(context).namespaceService.getAll().then((result) {
|
||||
setState(() {
|
||||
_loading = false;
|
||||
_namespaces = result;
|
||||
});
|
||||
});
|
||||
|
@ -121,11 +101,16 @@ class HomePageState extends State<HomePage> {
|
|||
),
|
||||
),
|
||||
new Expanded(
|
||||
child: ListView(
|
||||
padding: EdgeInsets.zero,
|
||||
children:
|
||||
ListTile.divideTiles(context: context, tiles: drawerOptions)
|
||||
.toList())),
|
||||
child: this._loading
|
||||
? Center(child: CircularProgressIndicator())
|
||||
: RefreshIndicator(
|
||||
child: ListView(
|
||||
padding: EdgeInsets.zero,
|
||||
children: ListTile.divideTiles(
|
||||
context: context, tiles: drawerOptions)
|
||||
.toList()),
|
||||
onRefresh: _updateNamespaces,
|
||||
)),
|
||||
new Align(
|
||||
alignment: FractionalOffset.bottomCenter,
|
||||
child: new ListTile(
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:vikunja_app/components/AddDialog.dart';
|
||||
import 'package:vikunja_app/components/TaskTile.dart';
|
||||
import 'package:vikunja_app/global.dart';
|
||||
import 'package:vikunja_app/models/task.dart';
|
||||
|
||||
|
@ -12,117 +16,99 @@ class ListPage extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _ListPageState extends State<ListPage> {
|
||||
TaskList items;
|
||||
TaskList _items;
|
||||
List<Task> _loadingTasks = [];
|
||||
bool _loading = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
items = TaskList(
|
||||
_items = TaskList(
|
||||
id: widget.taskList.id, title: widget.taskList.title, tasks: []);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: new Text(items.title),
|
||||
),
|
||||
body: ListView(
|
||||
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||
children: ListTile.divideTiles(
|
||||
context: context,
|
||||
tiles: items?.tasks?.map((task) => CheckboxListTile(
|
||||
title: Text(task.text),
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
value: task.done ?? false,
|
||||
subtitle: task.description == null
|
||||
? null
|
||||
: Text(task.description),
|
||||
onChanged: (bool value) => _updateTask(task, value),
|
||||
)) ??
|
||||
[])
|
||||
.toList(),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () => _addItemDialog(), child: Icon(Icons.add)),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
_updateList();
|
||||
}
|
||||
|
||||
_updateTask(Task task, bool checked) {
|
||||
// TODO use copyFrom
|
||||
VikunjaGlobal.of(context)
|
||||
.taskService
|
||||
.update(Task(
|
||||
id: task.id,
|
||||
done: checked,
|
||||
text: task.text,
|
||||
description: task.description,
|
||||
owner: null,
|
||||
))
|
||||
.then((_) => _updateList());
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: new Text(_items.title),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit),
|
||||
onPressed: () => {/* TODO add edit list functionality */},
|
||||
)
|
||||
],
|
||||
),
|
||||
body: !this._loading
|
||||
? RefreshIndicator(
|
||||
child: ListView(
|
||||
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||
children:
|
||||
ListTile.divideTiles(context: context, tiles: _listTasks())
|
||||
.toList(),
|
||||
),
|
||||
onRefresh: _updateList,
|
||||
)
|
||||
: Center(child: CircularProgressIndicator()),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () => _addItemDialog(), child: Icon(Icons.add)),
|
||||
);
|
||||
}
|
||||
|
||||
_updateList() {
|
||||
VikunjaGlobal.of(context).listService.get(widget.taskList.id).then((tasks) {
|
||||
List<Widget> _listTasks() {
|
||||
var tasks = (_items?.tasks?.map(_buildTile) ?? []).toList();
|
||||
tasks.addAll(_loadingTasks.map(_buildLoadingTile));
|
||||
return tasks;
|
||||
}
|
||||
|
||||
TaskTile _buildTile(Task task) {
|
||||
return TaskTile(task: task, loading: false);
|
||||
}
|
||||
|
||||
TaskTile _buildLoadingTile(Task task) {
|
||||
return TaskTile(
|
||||
task: task,
|
||||
loading: true,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _updateList() {
|
||||
return VikunjaGlobal.of(context)
|
||||
.listService
|
||||
.get(widget.taskList.id)
|
||||
.then((tasks) {
|
||||
setState(() {
|
||||
items = tasks;
|
||||
_loading = false;
|
||||
_items = tasks;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_addItemDialog() {
|
||||
var textController = new TextEditingController();
|
||||
showDialog(
|
||||
context: context,
|
||||
child: new AlertDialog(
|
||||
contentPadding: const EdgeInsets.all(16.0),
|
||||
content: new Row(children: <Widget>[
|
||||
Expanded(
|
||||
child: new TextField(
|
||||
autofocus: true,
|
||||
decoration: new InputDecoration(
|
||||
labelText: 'List Item', hintText: 'eg. Milk'),
|
||||
controller: textController,
|
||||
),
|
||||
)
|
||||
]),
|
||||
actions: <Widget>[
|
||||
new FlatButton(
|
||||
child: const Text('CANCEL'),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
new FlatButton(
|
||||
child: const Text('ADD'),
|
||||
onPressed: () {
|
||||
if (textController.text.isNotEmpty) _addItem(textController.text);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
context: context,
|
||||
builder: (_) => AddDialog(
|
||||
onAdd: _addItem,
|
||||
decoration: new InputDecoration(
|
||||
labelText: 'List Item', hintText: 'eg. Milk')));
|
||||
}
|
||||
|
||||
_addItem(String name) {
|
||||
var globalState = VikunjaGlobal.of(context);
|
||||
globalState.taskService
|
||||
.add(
|
||||
items.id,
|
||||
Task(
|
||||
id: null,
|
||||
text: name,
|
||||
owner: globalState.currentUser,
|
||||
done: false))
|
||||
.then((task) {
|
||||
var newTask =
|
||||
Task(id: null, text: name, owner: globalState.currentUser, done: false);
|
||||
setState(() => _loadingTasks.add(newTask));
|
||||
globalState.taskService.add(_items.id, newTask).then((task) {
|
||||
setState(() {
|
||||
items.tasks.add(task);
|
||||
_items.tasks.add(task);
|
||||
});
|
||||
}).then((_) => _updateList());
|
||||
}).then((_) => _updateList()
|
||||
.then((_) => setState(() => _loadingTasks.remove(newTask))));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,7 +99,6 @@ class _LoginPageState extends State<LoginPage> {
|
|||
await vGlobal.newLoginService(_server).login(_username, _password);
|
||||
vGlobal.changeUser(newUser.user, token: newUser.token, base: _server);
|
||||
} catch (ex) {
|
||||
print(ex);
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => new AlertDialog(
|
||||
|
|
Reference in New Issue