Skip to content

Commit 18fb0f2

Browse files
authored
Setup (#9)
* chore: configure .env * setup: core color theme
1 parent bac614f commit 18fb0f2

11 files changed

Lines changed: 811 additions & 110 deletions

File tree

src/lib/core/constants/.gitkeep

Whitespace-only changes.

src/lib/core/theme/app_colors.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import 'package:flutter/material.dart';
2+
3+
class AppColors {
4+
static const Color primaryBlue = Color(0xFF4A90E2);
5+
static const Color backgroundBlue = Color(0xFFE0F7FA);
6+
static const Color textLightBlue = Color(0xFFA6D4F1);
7+
static const Color taskCardBg = Colors.white;
8+
static const Color timeBoxBg = Color(0xFF2C3E50);
9+
static const Color grayText = Color(0xFF757575);
10+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import 'package:flutter/material.dart';
2+
import '../theme/app_colors.dart';
3+
4+
class CustomInputField extends StatelessWidget {
5+
final String label;
6+
final String hint;
7+
final TextEditingController controller;
8+
final int maxLines;
9+
10+
const CustomInputField({
11+
super.key,
12+
required this.label,
13+
required this.hint,
14+
required this.controller,
15+
this.maxLines = 1,
16+
});
17+
18+
@override
19+
Widget build(BuildContext context) {
20+
return Column(
21+
crossAxisAlignment: CrossAxisAlignment.start,
22+
children: [
23+
Text(label, style: Theme.of(context).textTheme.labelLarge),
24+
const SizedBox(height: 5),
25+
TextField(
26+
controller: controller,
27+
maxLines: maxLines,
28+
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
29+
decoration: InputDecoration(
30+
hintText: hint,
31+
hintStyle: TextStyle(color: Colors.grey.shade400, fontSize: 16),
32+
contentPadding: EdgeInsets.zero,
33+
enabledBorder: const UnderlineInputBorder(
34+
borderSide: BorderSide(color: Colors.black26)),
35+
focusedBorder: const UnderlineInputBorder(
36+
borderSide: BorderSide(color: AppColors.primaryBlue)),
37+
),
38+
),
39+
],
40+
);
41+
}
42+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import 'package:flutter/material.dart';
2+
3+
class TaskModel {
4+
final String id; // ID duy nhất để làm Hero tag và gọi API sau này
5+
String title;
6+
String description;
7+
String category;
8+
TimeOfDay startTime;
9+
TimeOfDay endTime;
10+
DateTime date;
11+
12+
TaskModel({
13+
required this.id,
14+
required this.title,
15+
required this.description,
16+
required this.category,
17+
required this.startTime,
18+
required this.endTime,
19+
required this.date,
20+
});
21+
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:intl/intl.dart';
3+
import '../../../../core/theme/app_colors.dart';
4+
import '../../../../core/widgets/custom_input_field.dart';
5+
import '../widgets/task_widgets.dart';
6+
7+
class CreateTaskScreen extends StatefulWidget {
8+
const CreateTaskScreen({super.key});
9+
10+
@override
11+
State<CreateTaskScreen> createState() => _CreateTaskScreenState();
12+
}
13+
14+
class _CreateTaskScreenState extends State<CreateTaskScreen> {
15+
final TextEditingController _nameController = TextEditingController(text: 'Team Meeting');
16+
final TextEditingController _descController = TextEditingController(text: 'Discuss all questions about new projects');
17+
DateTime _selectedDate = DateTime.now();
18+
TimeOfDay _startTime = const TimeOfDay(hour: 10, minute: 0);
19+
TimeOfDay _endTime = const TimeOfDay(hour: 11, minute: 0);
20+
int _selectedCategoryIndex = 0;
21+
22+
@override
23+
Widget build(BuildContext context) {
24+
String formattedDate = DateFormat('EEEE, d MMMM').format(_selectedDate);
25+
26+
return Scaffold(
27+
body: SafeArea(
28+
child: Column(
29+
children: [
30+
Container(
31+
decoration: BoxDecoration(
32+
color: Colors.white,
33+
borderRadius: const BorderRadius.only(
34+
bottomLeft: Radius.circular(30), bottomRight: Radius.circular(30)),
35+
boxShadow: [
36+
BoxShadow(
37+
color: Colors.black.withValues(alpha: 0.05),
38+
blurRadius: 10, offset: const Offset(0, 5))
39+
],
40+
),
41+
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
42+
child: Row(
43+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
44+
children: [
45+
IconButton(
46+
icon: const Icon(Icons.arrow_back_ios_new_rounded, color: Colors.black),
47+
onPressed: () => Navigator.pop(context),
48+
),
49+
const Icon(Icons.menu_rounded, color: Colors.black),
50+
const Icon(Icons.assignment_outlined, color: Colors.black),
51+
],
52+
),
53+
),
54+
Expanded(
55+
child: SingleChildScrollView(
56+
padding: const EdgeInsets.all(25.0),
57+
child: Column(
58+
crossAxisAlignment: CrossAxisAlignment.start,
59+
children: [
60+
Text('Create New Task', style: Theme.of(context).textTheme.headlineMedium),
61+
const SizedBox(height: 25),
62+
CustomInputField(label: 'Task Name', hint: 'Enter task name', controller: _nameController),
63+
const SizedBox(height: 20),
64+
Text('Select Category', style: Theme.of(context).textTheme.labelLarge),
65+
const SizedBox(height: 10),
66+
SizedBox(
67+
height: 40,
68+
child: ListView.builder(
69+
scrollDirection: Axis.horizontal,
70+
itemCount: 4,
71+
itemBuilder: (context, index) {
72+
List<String> categories = ['Development', 'Research', 'Design', 'Backend'];
73+
bool isSelected = index == _selectedCategoryIndex;
74+
return Padding(
75+
padding: const EdgeInsets.only(right: 10),
76+
child: ChoiceChip(
77+
label: Text(categories[index]),
78+
selected: isSelected,
79+
onSelected: (selected) => setState(() => _selectedCategoryIndex = selected ? index : 0),
80+
backgroundColor: const Color(0xFFF1F7FD),
81+
selectedColor: AppColors.primaryBlue,
82+
labelStyle: TextStyle(color: isSelected ? Colors.white : AppColors.primaryBlue, fontSize: 14),
83+
shape: RoundedRectangleBorder(
84+
borderRadius: BorderRadius.circular(10),
85+
side: const BorderSide(color: Color(0xFFF1F7FD), width: 1)),
86+
showCheckmark: false,
87+
),
88+
);
89+
},
90+
),
91+
),
92+
const SizedBox(height: 20),
93+
Row(
94+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
95+
children: [
96+
Column(
97+
crossAxisAlignment: CrossAxisAlignment.start,
98+
children: [
99+
Text('Date', style: Theme.of(context).textTheme.labelLarge),
100+
const SizedBox(height: 5),
101+
InkWell(
102+
onTap: () async {
103+
final DateTime? picked = await showDatePicker(
104+
context: context, initialDate: _selectedDate,
105+
firstDate: DateTime(2000), lastDate: DateTime(2100));
106+
if (picked != null) setState(() => _selectedDate = picked);
107+
},
108+
child: Column(
109+
crossAxisAlignment: CrossAxisAlignment.start,
110+
children: [
111+
Text(formattedDate, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
112+
const SizedBox(height: 5),
113+
Container(width: 150, height: 1, color: Colors.black26)
114+
],
115+
),
116+
)
117+
],
118+
),
119+
Container(
120+
padding: const EdgeInsets.all(12),
121+
decoration: BoxDecoration(color: AppColors.primaryBlue, borderRadius: BorderRadius.circular(15)),
122+
child: const Icon(Icons.date_range_rounded, color: Colors.white),
123+
)
124+
],
125+
),
126+
const SizedBox(height: 25),
127+
Row(
128+
children: [
129+
Expanded(
130+
child: Column(
131+
crossAxisAlignment: CrossAxisAlignment.start,
132+
children: [
133+
Text('Start time', style: Theme.of(context).textTheme.labelLarge),
134+
const SizedBox(height: 5),
135+
TimePickerWidget(time: _startTime, onChanged: (newTime) => setState(() => _startTime = newTime)),
136+
],
137+
),
138+
),
139+
const SizedBox(width: 20),
140+
Expanded(
141+
child: Column(
142+
crossAxisAlignment: CrossAxisAlignment.start,
143+
children: [
144+
Text('End time', style: Theme.of(context).textTheme.labelLarge),
145+
const SizedBox(height: 5),
146+
TimePickerWidget(time: _endTime, onChanged: (newTime) => setState(() => _endTime = newTime)),
147+
],
148+
),
149+
),
150+
],
151+
),
152+
const SizedBox(height: 25),
153+
CustomInputField(label: 'Description', hint: 'Enter task description', controller: _descController, maxLines: 2),
154+
const SizedBox(height: 40),
155+
Center(
156+
child: ElevatedButton(
157+
onPressed: () {},
158+
style: ElevatedButton.styleFrom(
159+
backgroundColor: AppColors.primaryBlue,
160+
foregroundColor: Colors.white,
161+
padding: const EdgeInsets.symmetric(horizontal: 100, vertical: 15),
162+
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
163+
),
164+
child: const Text('Create Task', style: TextStyle(fontSize: 18)),
165+
),
166+
),
167+
],
168+
),
169+
),
170+
),
171+
],
172+
),
173+
),
174+
);
175+
}
176+
}

0 commit comments

Comments
 (0)