Initial commit
This commit is contained in:
431
flutter_app/lib/screens/mobile/mobile_home_screen.dart
Normal file
431
flutter_app/lib/screens/mobile/mobile_home_screen.dart
Normal file
@@ -0,0 +1,431 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import '../../models/todo_item.dart';
|
||||
import '../../models/schedule_item.dart';
|
||||
import '../../models/announcement.dart';
|
||||
import '../../models/family_member.dart';
|
||||
import '../../services/todo_service.dart';
|
||||
import '../../services/schedule_service.dart';
|
||||
import '../../services/announcement_service.dart';
|
||||
import '../../services/family_service.dart';
|
||||
|
||||
class MobileHomeScreen extends StatefulWidget {
|
||||
const MobileHomeScreen({super.key});
|
||||
|
||||
@override
|
||||
State<MobileHomeScreen> createState() => _MobileHomeScreenState();
|
||||
}
|
||||
|
||||
class _MobileHomeScreenState extends State<MobileHomeScreen> {
|
||||
int _currentIndex = 0;
|
||||
|
||||
final List<Widget> _screens = [
|
||||
const MobileTodoScreen(),
|
||||
const MobileScheduleScreen(),
|
||||
const MobileAnnouncementScreen(),
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Bini Family Manager'),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.settings),
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(context, '/admin');
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: _screens[_currentIndex],
|
||||
bottomNavigationBar: BottomNavigationBar(
|
||||
currentIndex: _currentIndex,
|
||||
onTap: (index) {
|
||||
setState(() {
|
||||
_currentIndex = index;
|
||||
});
|
||||
},
|
||||
items: const [
|
||||
BottomNavigationBarItem(icon: Icon(Icons.check_box), label: 'Todos'),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.calendar_today),
|
||||
label: 'Schedule',
|
||||
),
|
||||
BottomNavigationBarItem(icon: Icon(Icons.campaign), label: 'Notices'),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MobileTodoScreen extends StatefulWidget {
|
||||
const MobileTodoScreen({super.key});
|
||||
|
||||
@override
|
||||
State<MobileTodoScreen> createState() => _MobileTodoScreenState();
|
||||
}
|
||||
|
||||
class _MobileTodoScreenState extends State<MobileTodoScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () => _showAddTodoDialog(context),
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
body: FutureBuilder<List<TodoItem>>(
|
||||
future: Provider.of<TodoService>(
|
||||
context,
|
||||
).fetchTodos(), // Fetch all or today? Let's fetch all for manager
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData)
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
final todos = snapshot.data!;
|
||||
return ListView.builder(
|
||||
itemCount: todos.length,
|
||||
itemBuilder: (context, index) {
|
||||
final todo = todos[index];
|
||||
return ListTile(
|
||||
title: Text(todo.title),
|
||||
subtitle: Text(
|
||||
todo.dueDate != null
|
||||
? DateFormat('MM/dd').format(todo.dueDate!)
|
||||
: 'No date',
|
||||
),
|
||||
trailing: Checkbox(
|
||||
value: todo.completed,
|
||||
onChanged: (val) async {
|
||||
await Provider.of<TodoService>(
|
||||
context,
|
||||
listen: false,
|
||||
).updateTodo(
|
||||
TodoItem(
|
||||
id: todo.id,
|
||||
familyMemberId: todo.familyMemberId,
|
||||
title: todo.title,
|
||||
completed: val ?? false,
|
||||
dueDate: todo.dueDate,
|
||||
),
|
||||
);
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
onLongPress: () async {
|
||||
await Provider.of<TodoService>(
|
||||
context,
|
||||
listen: false,
|
||||
).deleteTodo(todo.id);
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showAddTodoDialog(BuildContext context) async {
|
||||
final titleController = TextEditingController();
|
||||
final familyMembers = await Provider.of<FamilyService>(
|
||||
context,
|
||||
listen: false,
|
||||
).fetchFamilyMembers();
|
||||
String? selectedMemberId =
|
||||
familyMembers.isNotEmpty ? familyMembers.first.id : null;
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Add Todo'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextField(
|
||||
controller: titleController,
|
||||
decoration: const InputDecoration(labelText: 'Task'),
|
||||
),
|
||||
DropdownButtonFormField<String>(
|
||||
value: selectedMemberId,
|
||||
items: familyMembers
|
||||
.map(
|
||||
(m) => DropdownMenuItem(value: m.id, child: Text(m.name)),
|
||||
)
|
||||
.toList(),
|
||||
onChanged: (val) => selectedMemberId = val,
|
||||
decoration: const InputDecoration(labelText: 'Assign to'),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
if (selectedMemberId != null && titleController.text.isNotEmpty) {
|
||||
await Provider.of<TodoService>(
|
||||
context,
|
||||
listen: false,
|
||||
).createTodo(
|
||||
TodoItem(
|
||||
id: '',
|
||||
familyMemberId: selectedMemberId!,
|
||||
title: titleController.text,
|
||||
completed: false,
|
||||
dueDate: DateTime.now(),
|
||||
),
|
||||
);
|
||||
if (mounted) {
|
||||
Navigator.pop(context);
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
},
|
||||
child: const Text('Add'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MobileScheduleScreen extends StatefulWidget {
|
||||
const MobileScheduleScreen({super.key});
|
||||
|
||||
@override
|
||||
State<MobileScheduleScreen> createState() => _MobileScheduleScreenState();
|
||||
}
|
||||
|
||||
class _MobileScheduleScreenState extends State<MobileScheduleScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () => _showAddScheduleDialog(context),
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
body: FutureBuilder<List<ScheduleItem>>(
|
||||
future: Provider.of<ScheduleService>(context).fetchSchedules(),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData)
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
final schedules = snapshot.data!;
|
||||
return ListView.builder(
|
||||
itemCount: schedules.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = schedules[index];
|
||||
return ListTile(
|
||||
title: Text(item.title),
|
||||
subtitle: Text(
|
||||
'${DateFormat('MM/dd HH:mm').format(item.startDate)} - ${item.description}',
|
||||
),
|
||||
onLongPress: () async {
|
||||
await Provider.of<ScheduleService>(
|
||||
context,
|
||||
listen: false,
|
||||
).deleteSchedule(item.id);
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showAddScheduleDialog(BuildContext context) async {
|
||||
final titleController = TextEditingController();
|
||||
final descController = TextEditingController();
|
||||
final familyMembers = await Provider.of<FamilyService>(
|
||||
context,
|
||||
listen: false,
|
||||
).fetchFamilyMembers();
|
||||
String? selectedMemberId =
|
||||
familyMembers.isNotEmpty ? familyMembers.first.id : null;
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Add Schedule'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextField(
|
||||
controller: titleController,
|
||||
decoration: const InputDecoration(labelText: 'Title'),
|
||||
),
|
||||
TextField(
|
||||
controller: descController,
|
||||
decoration: const InputDecoration(labelText: 'Description'),
|
||||
),
|
||||
DropdownButtonFormField<String>(
|
||||
value: selectedMemberId,
|
||||
items: familyMembers
|
||||
.map(
|
||||
(m) => DropdownMenuItem(value: m.id, child: Text(m.name)),
|
||||
)
|
||||
.toList(),
|
||||
onChanged: (val) => selectedMemberId = val,
|
||||
decoration: const InputDecoration(labelText: 'For whom?'),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
if (selectedMemberId != null && titleController.text.isNotEmpty) {
|
||||
await Provider.of<ScheduleService>(
|
||||
context,
|
||||
listen: false,
|
||||
).createSchedule(
|
||||
ScheduleItem(
|
||||
id: '',
|
||||
title: titleController.text,
|
||||
description: descController.text,
|
||||
startDate: DateTime.now(),
|
||||
endDate: DateTime.now().add(const Duration(hours: 1)),
|
||||
familyMemberId: selectedMemberId!,
|
||||
isAllDay: false,
|
||||
),
|
||||
);
|
||||
if (mounted) {
|
||||
Navigator.pop(context);
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
},
|
||||
child: const Text('Add'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MobileAnnouncementScreen extends StatefulWidget {
|
||||
const MobileAnnouncementScreen({super.key});
|
||||
|
||||
@override
|
||||
State<MobileAnnouncementScreen> createState() =>
|
||||
_MobileAnnouncementScreenState();
|
||||
}
|
||||
|
||||
class _MobileAnnouncementScreenState extends State<MobileAnnouncementScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () => _showAddAnnouncementDialog(context),
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
body: FutureBuilder<List<Announcement>>(
|
||||
future: Provider.of<AnnouncementService>(context).fetchAnnouncements(),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData)
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
final items = snapshot.data!;
|
||||
return ListView.builder(
|
||||
itemCount: items.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = items[index];
|
||||
return ListTile(
|
||||
title: Text(item.title),
|
||||
subtitle: Text(item.content),
|
||||
trailing: Switch(
|
||||
value: item.active,
|
||||
onChanged: (val) async {
|
||||
await Provider.of<AnnouncementService>(
|
||||
context,
|
||||
listen: false,
|
||||
).updateAnnouncement(
|
||||
Announcement(
|
||||
id: item.id,
|
||||
title: item.title,
|
||||
content: item.content,
|
||||
priority: item.priority,
|
||||
active: val,
|
||||
),
|
||||
);
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
onLongPress: () async {
|
||||
await Provider.of<AnnouncementService>(
|
||||
context,
|
||||
listen: false,
|
||||
).deleteAnnouncement(item.id);
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showAddAnnouncementDialog(BuildContext context) {
|
||||
final titleController = TextEditingController();
|
||||
final contentController = TextEditingController();
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Add Announcement'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextField(
|
||||
controller: titleController,
|
||||
decoration: const InputDecoration(labelText: 'Title'),
|
||||
),
|
||||
TextField(
|
||||
controller: contentController,
|
||||
decoration: const InputDecoration(labelText: 'Content'),
|
||||
maxLines: 3,
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
if (titleController.text.isNotEmpty) {
|
||||
await Provider.of<AnnouncementService>(
|
||||
context,
|
||||
listen: false,
|
||||
).createAnnouncement(
|
||||
Announcement(
|
||||
id: '',
|
||||
title: titleController.text,
|
||||
content: contentController.text,
|
||||
priority: 1,
|
||||
active: true,
|
||||
),
|
||||
);
|
||||
if (mounted) {
|
||||
Navigator.pop(context);
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
},
|
||||
child: const Text('Add'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user