Categories page works!
This commit is contained in:
parent
0dd56cfaa6
commit
c9c3f69886
@ -1,30 +1,63 @@
|
|||||||
import 'package:appwrite/appwrite.dart';
|
import 'package:appwrite/appwrite.dart';
|
||||||
import 'package:appwrite/models.dart';
|
import 'package:appwrite/models.dart';
|
||||||
import 'package:ltx_flutter/constants/constants.dart';
|
import 'package:ltx_flutter/constants/constants.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:ltx_flutter/constants/colors.dart';
|
||||||
import 'package:ltx_flutter/appwrite/appwrite.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class Category {
|
||||||
|
late final Color backgroundColor;
|
||||||
|
late final Color foregroundColor;
|
||||||
|
late final num number;
|
||||||
|
late final num? parent;
|
||||||
|
late final String name;
|
||||||
|
late final String? description;
|
||||||
|
|
||||||
|
Category(Document doc) {
|
||||||
|
backgroundColor = _getBackgroundColor(doc.data['color']);
|
||||||
|
foregroundColor = _getForegroundColor(doc.data['color']);
|
||||||
|
number = doc.data['number'];
|
||||||
|
parent = doc.data['parent'];
|
||||||
|
name = doc.data['name'];
|
||||||
|
description = doc.data['description'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Color _getBackgroundColor(String colorStr) {
|
||||||
|
return Color(int.parse("0xff$colorStr"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Color _getForegroundColor(String colorStr) {
|
||||||
|
return Color(int.parse("0xff$colorStr")).computeLuminance() > 0.5
|
||||||
|
? Colors.black
|
||||||
|
: Colors.white;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasParent() {
|
||||||
|
return parent == number ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
double leftPadding() {
|
||||||
|
return hasParent() ? 50.0 : 20.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class CategoriesAPI extends ChangeNotifier {
|
class CategoriesAPI extends ChangeNotifier {
|
||||||
Client client = Client();
|
Client client = Client();
|
||||||
late final Account account;
|
late final Account account;
|
||||||
late final Databases databases;
|
late final Databases databases;
|
||||||
late final String userId;
|
late List<Document> _categories = [];
|
||||||
final AuthAPI auth = AuthAPI();
|
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
CategoriesAPI() {
|
CategoriesAPI() {
|
||||||
init();
|
init();
|
||||||
|
getCategories();
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadUser() async {
|
// Getters
|
||||||
// try {
|
get total => _categories.length;
|
||||||
// user = await account.get();
|
get all => _categories;
|
||||||
// notifyListeners();
|
get isEmpty => _categories.isEmpty;
|
||||||
// } catch (e) {
|
get(n) => Category(_categories[n]);
|
||||||
// print(e);
|
get colors => CategoryColor.values;
|
||||||
// notifyListeners();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
client.setEndpoint(APPWRITE_URL).setProject(APPWRITE_PROJECT_ID);
|
client.setEndpoint(APPWRITE_URL).setProject(APPWRITE_PROJECT_ID);
|
||||||
@ -32,34 +65,45 @@ class CategoriesAPI extends ChangeNotifier {
|
|||||||
databases = Databases(client);
|
databases = Databases(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<DocumentList> getCategories() {
|
getCategories() async {
|
||||||
print("Getting categories");
|
print("Updating categories.");
|
||||||
return databases.listDocuments(
|
_categories = [];
|
||||||
|
var response = await databases.listDocuments(
|
||||||
databaseId: CATEGORIES_DB,
|
databaseId: CATEGORIES_DB,
|
||||||
collectionId: COLLECTION,
|
collectionId: COLLECTION,
|
||||||
queries: [
|
queries: [
|
||||||
|
Query.orderAsc("parent"),
|
||||||
Query.orderAsc("number"),
|
Query.orderAsc("number"),
|
||||||
]);
|
]);
|
||||||
|
_categories = response.documents;
|
||||||
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Document> addCategory({
|
Future<Document>? addCategory({
|
||||||
required String name,
|
required String name,
|
||||||
required double number,
|
required double number,
|
||||||
required String color,
|
required String color,
|
||||||
String? description,
|
String? description,
|
||||||
int? parentId,
|
int? parentId,
|
||||||
}) {
|
}) async {
|
||||||
return databases.createDocument(
|
try {
|
||||||
databaseId: CATEGORIES_DB,
|
return await databases.createDocument(
|
||||||
collectionId: COLLECTION,
|
databaseId: CATEGORIES_DB,
|
||||||
documentId: "category-${number.toString()}",
|
collectionId: COLLECTION,
|
||||||
data: {
|
documentId: "category-${number.toString()}",
|
||||||
'name': name,
|
data: {
|
||||||
'number': number,
|
'name': name,
|
||||||
'color': color,
|
'number': number,
|
||||||
'parent': parentId,
|
'color': color,
|
||||||
'description': description,
|
'parent': parentId,
|
||||||
});
|
'description': description,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
throw "Didn't work";
|
||||||
|
} finally {
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<dynamic> deleteCategory({required double number}) {
|
Future<dynamic> deleteCategory({required double number}) {
|
||||||
|
|||||||
@ -1,19 +1,28 @@
|
|||||||
import 'package:appwrite/appwrite.dart';
|
import 'package:appwrite/appwrite.dart';
|
||||||
import 'package:appwrite/models.dart';
|
import 'package:appwrite/models.dart';
|
||||||
import 'package:ltx_flutter/appwrite/appwrite.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:ltx_flutter/appwrite/auth_api.dart';
|
||||||
import 'package:ltx_flutter/constants/constants.dart';
|
import 'package:ltx_flutter/constants/constants.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
class DatabaseAPI {
|
class DatabaseAPI extends ChangeNotifier {
|
||||||
Client client = Client();
|
Client client = Client();
|
||||||
late final Account account;
|
late final Account account;
|
||||||
late final Databases databases;
|
late final Databases databases;
|
||||||
final AuthAPI auth = AuthAPI();
|
final AuthAPI auth = AuthAPI();
|
||||||
|
|
||||||
|
late List<Document> _entries = [];
|
||||||
|
|
||||||
|
// Getter methods
|
||||||
|
List<Document> get entries => _entries;
|
||||||
|
int? get total => _entries.length;
|
||||||
|
|
||||||
final DateFormat formatter = DateFormat('yyyy-MM-dd');
|
final DateFormat formatter = DateFormat('yyyy-MM-dd');
|
||||||
|
|
||||||
|
// Constructor
|
||||||
DatabaseAPI() {
|
DatabaseAPI() {
|
||||||
init();
|
init();
|
||||||
|
getEntries();
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
@ -25,7 +34,7 @@ class DatabaseAPI {
|
|||||||
databases = Databases(client);
|
databases = Databases(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<DocumentList> getEntries({int limit = 100, String dateISO = ""}) {
|
getEntries({int limit = 100, String dateISO = ""}) async {
|
||||||
if (dateISO == "") {
|
if (dateISO == "") {
|
||||||
dateISO = DateTime.now().toIso8601String();
|
dateISO = DateTime.now().toIso8601String();
|
||||||
}
|
}
|
||||||
@ -37,7 +46,7 @@ class DatabaseAPI {
|
|||||||
|
|
||||||
print("Getting $limit entries starting from $offset");
|
print("Getting $limit entries starting from $offset");
|
||||||
|
|
||||||
return databases.listDocuments(
|
var response = await databases.listDocuments(
|
||||||
databaseId: APPWRITE_DATABASE_ID,
|
databaseId: APPWRITE_DATABASE_ID,
|
||||||
collectionId: COLLECTION,
|
collectionId: COLLECTION,
|
||||||
queries: [
|
queries: [
|
||||||
@ -45,6 +54,8 @@ class DatabaseAPI {
|
|||||||
Query.offset(offset),
|
Query.offset(offset),
|
||||||
Query.limit(limit),
|
Query.limit(limit),
|
||||||
]);
|
]);
|
||||||
|
_entries = response.documents;
|
||||||
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Document> addEntry(
|
Future<Document> addEntry(
|
||||||
|
|||||||
33
ltx_flutter/lib/constants/colors.dart
Normal file
33
ltx_flutter/lib/constants/colors.dart
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
enum CategoryColor {
|
||||||
|
black("273036"),
|
||||||
|
blue("00a9b3"),
|
||||||
|
red("c71634"),
|
||||||
|
darkred("ff2816"),
|
||||||
|
lime("bfff55"),
|
||||||
|
green("189749"),
|
||||||
|
pink("ff65ae"),
|
||||||
|
purple("5b3ab1"),
|
||||||
|
cyan("005744"),
|
||||||
|
orange("ff6d01"),
|
||||||
|
yellow("fff336");
|
||||||
|
|
||||||
|
const CategoryColor(this.hex);
|
||||||
|
|
||||||
|
final String hex;
|
||||||
|
|
||||||
|
name() {
|
||||||
|
return this.toString().split('.').last;
|
||||||
|
}
|
||||||
|
|
||||||
|
backgroundColor() {
|
||||||
|
return Color(int.parse("0xff$hex"));
|
||||||
|
}
|
||||||
|
|
||||||
|
foregroundColor() {
|
||||||
|
return Color(int.parse("0xff$hex")).computeLuminance() > 0.3
|
||||||
|
? Colors.black
|
||||||
|
: Colors.white;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,12 +1,19 @@
|
|||||||
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:ltx_flutter/pages/tabs_page.dart';
|
import 'package:ltx_flutter/pages/tabs_page.dart';
|
||||||
import 'package:ltx_flutter/appwrite/appwrite.dart';
|
import 'package:ltx_flutter/appwrite/auth_api.dart';
|
||||||
|
import 'package:ltx_flutter/appwrite/database_api.dart';
|
||||||
|
import 'package:ltx_flutter/appwrite/categories_api.dart';
|
||||||
import 'package:ltx_flutter/pages/login_page.dart';
|
import 'package:ltx_flutter/pages/login_page.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(ChangeNotifierProvider(
|
runApp(MultiProvider(
|
||||||
create: (context) => AuthAPI(),
|
providers: [
|
||||||
|
ChangeNotifierProvider(create: (context) => AuthAPI()),
|
||||||
|
ChangeNotifierProvider(create: (context) => DatabaseAPI()),
|
||||||
|
ChangeNotifierProvider(create: (context) => CategoriesAPI()),
|
||||||
|
],
|
||||||
child: LifetrackerApp(),
|
child: LifetrackerApp(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -16,19 +23,22 @@ class LifetrackerApp extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final value = context.watch<AuthAPI>().status;
|
final loginStatus = context.watch<AuthAPI>().status;
|
||||||
print('TOP CHANGE Value changed to: $value!');
|
|
||||||
|
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
|
scrollBehavior: const MaterialScrollBehavior().copyWith(dragDevices: {
|
||||||
|
PointerDeviceKind.mouse,
|
||||||
|
PointerDeviceKind.touch,
|
||||||
|
PointerDeviceKind.trackpad,
|
||||||
|
}),
|
||||||
title: 'Lifetracker',
|
title: 'Lifetracker',
|
||||||
theme: ThemeData.from(
|
theme: ThemeData.from(
|
||||||
colorScheme: ColorScheme.dark(),
|
colorScheme: ColorScheme.dark(),
|
||||||
),
|
),
|
||||||
home: value == AuthStatus.uninitialized
|
home: loginStatus == AuthStatus.uninitialized
|
||||||
? const Scaffold(
|
? const Scaffold(
|
||||||
body: Center(child: CircularProgressIndicator()),
|
body: Center(child: CircularProgressIndicator()),
|
||||||
)
|
)
|
||||||
: value == AuthStatus.authenticated
|
: loginStatus == AuthStatus.authenticated
|
||||||
? const TabsPage()
|
? const TabsPage()
|
||||||
: LoginPage());
|
: LoginPage());
|
||||||
// DefaultTabController(
|
// DefaultTabController(
|
||||||
@ -55,88 +65,3 @@ class LifetrackerApp extends StatelessWidget {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AccountPage extends StatefulWidget {
|
|
||||||
const AccountPage({
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
AccountPageState createState() {
|
|
||||||
return AccountPageState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AccountPageState extends State<AccountPage> {
|
|
||||||
final _loginFormKey = GlobalKey<FormState>();
|
|
||||||
String email = "";
|
|
||||||
String password = "";
|
|
||||||
|
|
||||||
void _logIn(context) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
const SnackBar(content: Text('Logging in...')),
|
|
||||||
);
|
|
||||||
// client.login(email, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.all(30.0),
|
|
||||||
child: Form(
|
|
||||||
key: _loginFormKey,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
SizedBox(
|
|
||||||
width: 250,
|
|
||||||
child: TextFormField(
|
|
||||||
obscureText: false,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
border: OutlineInputBorder(),
|
|
||||||
labelText: 'Email',
|
|
||||||
),
|
|
||||||
keyboardType: TextInputType.emailAddress,
|
|
||||||
onChanged: (value) => setState(() {
|
|
||||||
email = value;
|
|
||||||
}),
|
|
||||||
)),
|
|
||||||
SizedBox(
|
|
||||||
height: 20,
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: 250,
|
|
||||||
child: TextFormField(
|
|
||||||
obscureText: true,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
border: OutlineInputBorder(),
|
|
||||||
labelText: 'Password',
|
|
||||||
),
|
|
||||||
keyboardType: TextInputType.visiblePassword,
|
|
||||||
onChanged: (value) => setState(() {
|
|
||||||
password = value;
|
|
||||||
}),
|
|
||||||
)),
|
|
||||||
SizedBox(
|
|
||||||
height: 20,
|
|
||||||
),
|
|
||||||
ElevatedButton.icon(
|
|
||||||
onPressed: () {
|
|
||||||
_logIn(context);
|
|
||||||
},
|
|
||||||
icon: Icon(Icons.login),
|
|
||||||
label: Text("Log in")),
|
|
||||||
Column(
|
|
||||||
children: <Widget>[
|
|
||||||
email.isEmpty ? Text("No data") : Text(email),
|
|
||||||
SizedBox(
|
|
||||||
height: 10,
|
|
||||||
),
|
|
||||||
password.isEmpty ? Text("No Data") : Text(password),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,4 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:ltx_flutter/appwrite/auth_api.dart';
|
||||||
|
import 'package:ltx_flutter/appwrite/database_api.dart';
|
||||||
|
import 'package:ltx_flutter/appwrite/categories_api.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class AccountPage extends StatefulWidget {
|
class AccountPage extends StatefulWidget {
|
||||||
const AccountPage({Key? key}) : super(key: key);
|
const AccountPage({Key? key}) : super(key: key);
|
||||||
@ -10,8 +14,47 @@ class AccountPage extends StatefulWidget {
|
|||||||
class _AccountPageState extends State<AccountPage> {
|
class _AccountPageState extends State<AccountPage> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Column(
|
||||||
body: Center(child: Text("Account")),
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: ListView(
|
||||||
|
children: [
|
||||||
|
Card(
|
||||||
|
child: Consumer<AuthAPI>(builder: (context, account, child) {
|
||||||
|
return ListTile(
|
||||||
|
leading: Icon(Icons.person),
|
||||||
|
title: Text("Account"),
|
||||||
|
trailing: Text("${account.username}"),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Card(
|
||||||
|
child:
|
||||||
|
Consumer<DatabaseAPI>(builder: (context, entries, child) {
|
||||||
|
return ListTile(
|
||||||
|
leading: Icon(Icons.edit_note),
|
||||||
|
title: Text("Entries"),
|
||||||
|
trailing: Text("${entries.total}"),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Card(
|
||||||
|
child: Consumer<CategoriesAPI>(
|
||||||
|
builder: (context, categories, child) {
|
||||||
|
return ListTile(
|
||||||
|
leading: Icon(Icons.category),
|
||||||
|
title: Text("Categories"),
|
||||||
|
trailing: Text("${categories.total}"),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Placeholder(),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import 'package:ltx_flutter/appwrite/categories_api.dart';
|
import 'package:ltx_flutter/appwrite/categories_api.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:appwrite/models.dart';
|
import 'package:appwrite/models.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||||
|
|
||||||
class CategoriesPage extends StatefulWidget {
|
class CategoriesPage extends StatefulWidget {
|
||||||
const CategoriesPage({Key? key}) : super(key: key);
|
const CategoriesPage({Key? key}) : super(key: key);
|
||||||
@ -10,53 +12,218 @@ class CategoriesPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _CategoriesPageState extends State<CategoriesPage> {
|
class _CategoriesPageState extends State<CategoriesPage> {
|
||||||
final api = CategoriesAPI();
|
|
||||||
late List<Document>? categories = [];
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
loadCategories();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future loadCategories() async {
|
|
||||||
try {
|
|
||||||
final value = await api.getCategories();
|
|
||||||
setState(() {
|
|
||||||
categories = value.documents;
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
print(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return RefreshIndicator(
|
return Scaffold(
|
||||||
onRefresh: loadCategories,
|
floatingActionButton: FloatingActionButton(
|
||||||
child: Center(
|
onPressed: () => Navigator.push(
|
||||||
child: ListView.builder(
|
context,
|
||||||
itemCount: categories!.length + 1,
|
MaterialPageRoute(builder: (context) => const NewCategoryPage()),
|
||||||
itemBuilder: (context, index) {
|
).then((value) {
|
||||||
if (index >= categories!.length || categories!.isEmpty) {
|
setState(() {});
|
||||||
return ListTile(
|
}),
|
||||||
leading: Text("New Category"),
|
tooltip: "New Category",
|
||||||
);
|
child: Icon(Icons.add),
|
||||||
} else {
|
),
|
||||||
Document? category = categories?[index];
|
body: Consumer<CategoriesAPI>(builder: (context, categories, child) {
|
||||||
Color backgroundColor =
|
return ListView.builder(
|
||||||
Color(int.parse("0x${category!.data['color']}"));
|
itemCount: categories.total + 1,
|
||||||
Color textColor = backgroundColor.computeLuminance() > 0.2
|
itemBuilder: (context, i) {
|
||||||
? Colors.black
|
if (i < categories.total) {
|
||||||
: Colors.white;
|
Category category = categories.get(i);
|
||||||
return ListTile(
|
if (!category.hasParent() && category.number != 0) {
|
||||||
tileColor: backgroundColor,
|
return Column(
|
||||||
textColor: textColor,
|
children: [
|
||||||
leading: Text(category.data['number'].toString()),
|
Divider(
|
||||||
title: Text(category.data['name']),
|
thickness: 1,
|
||||||
);
|
),
|
||||||
|
CategoryRow(category: category),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return CategoryRow(category: category);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CategoryRow extends StatelessWidget {
|
||||||
|
const CategoryRow({
|
||||||
|
super.key,
|
||||||
|
required this.category,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Category category;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
color: category.backgroundColor,
|
||||||
|
child: SizedBox.square(
|
||||||
|
dimension: 70,
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: category.foregroundColor,
|
||||||
|
fontSize: 18,
|
||||||
|
),
|
||||||
|
category.number.toString())),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.only(left: category.leftPadding()),
|
||||||
|
child: Text(
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 17,
|
||||||
|
),
|
||||||
|
category.name,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(15.0),
|
||||||
|
child: Text(
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
color: Colors.white38,
|
||||||
|
),
|
||||||
|
"${category.description}",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NewCategoryPage extends StatefulWidget {
|
||||||
|
const NewCategoryPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<NewCategoryPage> createState() => _NewCategoryPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NewCategoryPageState extends State<NewCategoryPage> {
|
||||||
|
String _name = "";
|
||||||
|
final _formKey = GlobalKey<FormBuilderState>();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_name = "New Category";
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final CategoriesAPI api = context.watch<CategoriesAPI>();
|
||||||
|
saveCategory(form, context) {
|
||||||
|
var name = form?.fields['categoryName'].value;
|
||||||
|
var number = form?.fields['categoryNumber'].value;
|
||||||
|
var color = form?.fields['categoryColor'].value;
|
||||||
|
var description = form?.fields['categoryDescription'].value;
|
||||||
|
var parent = form?.fields['categoryParent'].value;
|
||||||
|
api.addCategory(
|
||||||
|
name: name,
|
||||||
|
number: double.parse(number),
|
||||||
|
color: color,
|
||||||
|
description: description,
|
||||||
|
parentId: int.parse(parent),
|
||||||
|
);
|
||||||
|
Navigator.pop(context);
|
||||||
|
api.getCategories();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text("New Category"),
|
||||||
|
),
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 50, right: 50),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
FormBuilder(
|
||||||
|
key: _formKey,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(height: 25),
|
||||||
|
FormBuilderTextField(
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: "Number",
|
||||||
|
),
|
||||||
|
name: 'categoryNumber',
|
||||||
|
),
|
||||||
|
SizedBox(height: 25),
|
||||||
|
FormBuilderTextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: "Name",
|
||||||
|
),
|
||||||
|
name: 'categoryName',
|
||||||
|
),
|
||||||
|
SizedBox(height: 25),
|
||||||
|
Consumer<CategoriesAPI>(
|
||||||
|
builder: (context, categories, child) {
|
||||||
|
print(categories.colors);
|
||||||
|
return FormBuilderDropdown(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: "Color",
|
||||||
|
),
|
||||||
|
items: [
|
||||||
|
for (var c in categories.colors)
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: c.hex,
|
||||||
|
child: Container(
|
||||||
|
decoration:
|
||||||
|
BoxDecoration(color: c.backgroundColor()),
|
||||||
|
padding: EdgeInsets.all(20),
|
||||||
|
child: Text(
|
||||||
|
c.name(),
|
||||||
|
style: TextStyle(color: c.foregroundColor()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
name: 'categoryColor',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(height: 25),
|
||||||
|
FormBuilderTextField(
|
||||||
|
name: 'categoryDescription',
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: "Description",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 25),
|
||||||
|
FormBuilderTextField(
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
name: 'categoryParent',
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: "Parent",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 40),
|
||||||
|
OutlinedButton(
|
||||||
|
onPressed: () => saveCategory(_formKey.currentState, context),
|
||||||
|
child: Text("Save"),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import 'package:appwrite/appwrite.dart';
|
import 'package:appwrite/appwrite.dart';
|
||||||
import 'package:ltx_flutter/appwrite/appwrite.dart';
|
import 'package:ltx_flutter/appwrite/auth_api.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:spreadsheet_table/spreadsheet_table.dart';
|
import 'package:spreadsheet_table/spreadsheet_table.dart';
|
||||||
import 'package:pluto_grid/pluto_grid.dart';
|
import 'package:pluto_grid/pluto_grid.dart';
|
||||||
import 'package:ltx_flutter/appwrite/appwrite.dart';
|
import 'package:ltx_flutter/appwrite/auth_api.dart';
|
||||||
import 'package:ltx_flutter/appwrite/database_api.dart';
|
import 'package:ltx_flutter/appwrite/database_api.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:appwrite/models.dart';
|
import 'package:appwrite/models.dart';
|
||||||
|
|||||||
@ -11,7 +11,7 @@ class TabsPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _TabsPageState extends State<TabsPage> {
|
class _TabsPageState extends State<TabsPage> {
|
||||||
int _selectedIndex = 0;
|
int _selectedIndex = 2;
|
||||||
|
|
||||||
static const _widgets = [TodayPage(), CategoriesPage(), AccountPage()];
|
static const _widgets = [TodayPage(), CategoriesPage(), AccountPage()];
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ class _TabsPageState extends State<TabsPage> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text("LTX Android"),
|
title: Text("Flutter"),
|
||||||
),
|
),
|
||||||
body: _widgets.elementAt(_selectedIndex),
|
body: _widgets.elementAt(_selectedIndex),
|
||||||
bottomNavigationBar: BottomNavigationBar(
|
bottomNavigationBar: BottomNavigationBar(
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import 'package:ltx_flutter/appwrite/appwrite.dart';
|
import 'dart:math' as math;
|
||||||
|
import 'package:ltx_flutter/appwrite/auth_api.dart';
|
||||||
import 'package:ltx_flutter/appwrite/database_api.dart';
|
import 'package:ltx_flutter/appwrite/database_api.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
@ -92,6 +93,38 @@ class _TodayPageState extends State<TodayPage> {
|
|||||||
},
|
},
|
||||||
).toList()),
|
).toList()),
|
||||||
),
|
),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.only(top: 10),
|
||||||
|
color: Colors.black,
|
||||||
|
child: Row(
|
||||||
|
children: List<int>.generate(10, (i) => i).map(
|
||||||
|
(e) {
|
||||||
|
var generatedColor =
|
||||||
|
math.Random().nextInt(Colors.primaries.length);
|
||||||
|
return Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(2.0),
|
||||||
|
child: FilledButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
shape: MaterialStateProperty.all<
|
||||||
|
RoundedRectangleBorder>(
|
||||||
|
RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.zero,
|
||||||
|
side: BorderSide(color: Colors.white))),
|
||||||
|
backgroundColor: MaterialStateProperty.all(
|
||||||
|
Colors.primaries[generatedColor]),
|
||||||
|
),
|
||||||
|
onLongPress: () =>
|
||||||
|
print("Long pressed ${e.toString()}"),
|
||||||
|
onPressed: () => print("Tapped ${e.toString()}"),
|
||||||
|
child: Text(e.toString()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -18,6 +18,7 @@ dependencies:
|
|||||||
intl: ^0.18.1
|
intl: ^0.18.1
|
||||||
pluto_grid: ^7.0.2
|
pluto_grid: ^7.0.2
|
||||||
string_to_hex: ^0.2.2
|
string_to_hex: ^0.2.2
|
||||||
|
flutter_form_builder: ^9.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user