Skip to content

Commit e72ce94

Browse files
feat: add light mode with persistence
- Added `shared_preferences` dependency. - Updated `ThemeCubit` to save and load theme mode from local storage. - Ensured theme preference persists across app restarts. - Refactored UI to use dynamic theme. - Added light mode color scheme. - Added theme toggle in Account settings. Co-authored-by: Salint <41103301+Salint@users.noreply.github.com>
1 parent c543a53 commit e72ce94

File tree

7 files changed

+182
-113
lines changed

7 files changed

+182
-113
lines changed

mobile/lib/app/theme_cubit.dart

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
11
import 'package:flutter/material.dart';
22
import 'package:flutter_bloc/flutter_bloc.dart';
3+
import 'package:shared_preferences/shared_preferences.dart';
34

45
class ThemeCubit extends Cubit<ThemeMode> {
5-
ThemeCubit() : super(ThemeMode.dark);
6+
ThemeCubit() : super(ThemeMode.dark) {
7+
_loadTheme();
8+
}
9+
10+
void _loadTheme() async {
11+
var prefs = await SharedPreferences.getInstance();
12+
var isDark = prefs.getBool('isDark') ?? true;
13+
emit(isDark ? ThemeMode.dark : ThemeMode.light);
14+
}
615

7-
void toggleTheme(bool isDark) {
16+
void toggleTheme(bool isDark) async {
17+
var prefs = await SharedPreferences.getInstance();
18+
await prefs.setBool('isDark', isDark);
819
emit(isDark ? ThemeMode.dark : ThemeMode.light);
920
}
1021
}

mobile/lib/features/account/presentation/pages/account.dart

Lines changed: 17 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,9 @@ class AccountPage extends StatelessWidget {
3636
decoration: BoxDecoration(
3737
color: Theme.of(context).colorScheme.surfaceContainer,
3838
gradient: LinearGradient(
39-
colors: [
40-
Theme.of(context).colorScheme.primary,
41-
Theme.of(context).colorScheme.secondary
42-
],
39+
colors: [Theme.of(context).colorScheme.primary, Theme.of(context).colorScheme.secondary],
4340
),
44-
borderRadius:
45-
const BorderRadius.all(Radius.circular(10))),
41+
borderRadius: const BorderRadius.all(Radius.circular(10))),
4642
child: Row(
4743
spacing: 10,
4844
children: [
@@ -72,51 +68,32 @@ class AccountPage extends StatelessWidget {
7268
},
7369
errorBuilder: (BuildContext context, Object error,
7470
StackTrace? stackTrace) {
75-
return Icon(
76-
Icons.person,
77-
size: 60,
78-
color: Theme.of(context).colorScheme.onPrimary,
79-
);
71+
return const Icon(Icons.person, size: 60);
8072
},
8173
),
8274
)
8375
else
84-
Icon(
85-
Icons.person,
86-
size: 60,
87-
color: Theme.of(context).colorScheme.onPrimary,
88-
),
76+
const Icon(Icons.person, size: 60),
8977
Flexible(
9078
child: Column(
9179
crossAxisAlignment: CrossAxisAlignment.start,
9280
children: [
9381
if (user.name.isNotEmpty)
9482
Text(
9583
user.name,
96-
style: TextStyle(
97-
fontWeight: FontWeight.bold,
98-
fontSize: 24,
99-
color:
100-
Theme.of(context).colorScheme.onPrimary,
101-
),
84+
style: const TextStyle(
85+
fontWeight: FontWeight.bold,
86+
fontSize: 24),
10287
)
10388
else
104-
Text(
89+
const Text(
10590
"No display name",
10691
style: TextStyle(
107-
fontWeight: FontWeight.normal,
108-
fontSize: 24,
109-
fontStyle: FontStyle.italic,
110-
color:
111-
Theme.of(context).colorScheme.onPrimary,
112-
),
92+
fontWeight: FontWeight.normal,
93+
fontSize: 24,
94+
fontStyle: FontStyle.italic),
11395
),
114-
Text(
115-
user.email,
116-
style: TextStyle(
117-
color: Theme.of(context).colorScheme.onPrimary,
118-
),
119-
)
96+
Text(user.email)
12097
],
12198
),
12299
)
@@ -128,7 +105,7 @@ class AccountPage extends StatelessWidget {
128105
),
129106
Container(
130107
decoration: BoxDecoration(
131-
color: Theme.of(context).colorScheme.surfaceContainer,
108+
color: Theme.of(context).colorScheme.surfaceBright,
132109
borderRadius: const BorderRadius.all(Radius.circular(10)),
133110
),
134111
child: Column(
@@ -195,15 +172,13 @@ class AccountPage extends StatelessWidget {
195172
),
196173
Container(
197174
width: double.infinity,
198-
padding: const EdgeInsets.symmetric(
199-
horizontal: 15, vertical: 5),
175+
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 5),
200176
child: Row(
201177
mainAxisAlignment: MainAxisAlignment.spaceBetween,
202178
children: [
203179
const Text("Dark Mode"),
204180
Switch(
205-
value: context.read<ThemeCubit>().state ==
206-
ThemeMode.dark,
181+
value: context.read<ThemeCubit>().state == ThemeMode.dark,
207182
onChanged: (val) {
208183
context.read<ThemeCubit>().toggleTheme(val);
209184
},
@@ -225,7 +200,7 @@ class AccountPage extends StatelessWidget {
225200
width: double.infinity,
226201
padding: const EdgeInsets.all(15),
227202
decoration: BoxDecoration(
228-
color: Theme.of(context).colorScheme.surfaceContainer,
203+
color: Theme.of(context).colorScheme.surfaceBright,
229204
borderRadius: const BorderRadius.all(Radius.circular(10)),
230205
),
231206
child: const Text(
@@ -243,9 +218,7 @@ class AccountPage extends StatelessWidget {
243218
Text(
244219
"(c) ${DateTime.now().year} TwoAxis. All Rights Reserved.",
245220
textAlign: TextAlign.center,
246-
style: TextStyle(
247-
color:
248-
Theme.of(context).colorScheme.onSurfaceVariant),
221+
style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
249222
),
250223
],
251224
),

mobile/lib/features/app_shell/presentation/widgets/navbar.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class Navbar extends StatelessWidget {
2727
showUnselectedLabels: true,
2828
selectedItemColor: Theme.of(context).colorScheme.primary,
2929
type: BottomNavigationBarType.fixed,
30-
unselectedItemColor: Theme.of(context).colorScheme.onSurfaceVariant,
30+
unselectedItemColor: Color(0x44FFFFFF),
3131
onTap: onTap,
3232
items: [
3333
BottomNavigationBarItem(

mobile/lib/features/dashboard/presentation/pages/dashboard.dart

Lines changed: 43 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import 'package:twoaxis_finance/app/theme_cubit.dart';
21
import 'package:twoaxis_finance/features/dashboard/presentation/widgets/dashboard_button.dart';
32
import 'package:twoaxis_finance/features/transactions/domain/transaction_type.dart';
43
import 'package:twoaxis_finance/features/transactions/presentation/bloc/transactions_bloc.dart';
@@ -27,26 +26,23 @@ class _DashboardPageState extends State<DashboardPage> {
2726

2827
@override
2928
Widget build(BuildContext context) {
30-
return BlocBuilder<UserCubit, UserState>(builder: (context, userState) {
31-
var user = (userState as UserStateAuthenticated).user;
29+
return BlocBuilder<UserCubit, UserState>(
30+
builder: (context, userState) {
31+
32+
if(userState is !UserStateAuthenticated) return const SizedBox();
3233

33-
return BlocBuilder<ThemeCubit, ThemeMode>(builder: (context, mode) {
3434
return Stack(
3535
children: [
36-
if (mode == ThemeMode.dark)
37-
Container(
38-
height: 500,
39-
decoration: BoxDecoration(
40-
gradient: RadialGradient(
41-
colors: [
42-
Theme.of(context).colorScheme.primary,
43-
Colors.transparent
44-
],
45-
radius: 1,
46-
center: Alignment.topCenter,
47-
),
36+
Container(
37+
height: 500,
38+
decoration: BoxDecoration(
39+
gradient: RadialGradient(
40+
colors: [Theme.of(context).colorScheme.primary, Colors.transparent],
41+
radius: 1,
42+
center: Alignment.topCenter,
4843
),
4944
),
45+
),
5046
Container(
5147
padding: const EdgeInsets.all(20),
5248
child: Column(
@@ -72,9 +68,7 @@ class _DashboardPageState extends State<DashboardPage> {
7268
width: 120,
7369
height: 40,
7470
decoration: BoxDecoration(
75-
color: Theme.of(context)
76-
.colorScheme
77-
.surfaceContainer,
71+
color: Theme.of(context).colorScheme.surfaceContainer,
7872
borderRadius: BorderRadius.circular(5),
7973
),
8074
),
@@ -83,15 +77,10 @@ class _DashboardPageState extends State<DashboardPage> {
8377
Text(
8478
formatMoneyWithContext(
8579
context,
86-
user.balances.fold<double>(0,
87-
(sum, balance) => sum + balance.value)),
88-
style: TextStyle(
89-
fontWeight: FontWeight.bold,
90-
fontSize: 40,
91-
color: mode == ThemeMode.dark
92-
? Theme.of(context).colorScheme.onSurface
93-
: Theme.of(context).colorScheme.primary,
94-
),
80+
userState.user.balances.fold<double>(
81+
0, (sum, balance) => sum + balance.value)),
82+
style: const TextStyle(
83+
fontWeight: FontWeight.bold, fontSize: 40),
9584
),
9685
IconButton(
9786
onPressed: () {
@@ -135,7 +124,7 @@ class _DashboardPageState extends State<DashboardPage> {
135124
decoration: BoxDecoration(
136125
color: Colors.grey[300],
137126
borderRadius:
138-
BorderRadius.circular(2),
127+
BorderRadius.circular(2),
139128
),
140129
),
141130
const SizedBox(
@@ -175,8 +164,7 @@ class _DashboardPageState extends State<DashboardPage> {
175164
Navigator.push(
176165
context,
177166
MaterialPageRoute(
178-
builder: (context) =>
179-
const AnalyticsPage()));
167+
builder: (context) => const AnalyticsPage()));
180168
},
181169
),
182170
DashboardButton(
@@ -186,8 +174,7 @@ class _DashboardPageState extends State<DashboardPage> {
186174
Navigator.push(
187175
context,
188176
MaterialPageRoute(
189-
builder: (context) =>
190-
const BudgetPage()));
177+
builder: (context) => const BudgetPage()));
191178
},
192179
),
193180
],
@@ -211,9 +198,9 @@ class _DashboardPageState extends State<DashboardPage> {
211198
children: [
212199
Expanded(
213200
child: Text(
214-
"Recent Transactions",
215-
style: TextStyle(fontSize: 15),
216-
)),
201+
"Recent Transactions",
202+
style: TextStyle(fontSize: 15),
203+
)),
217204
Icon(Icons.arrow_forward_ios)
218205
],
219206
),
@@ -228,13 +215,11 @@ class _DashboardPageState extends State<DashboardPage> {
228215
return const Center(
229216
child: CircularProgressIndicator(),
230217
);
231-
} else if (transactionsState
232-
is TransactionsStateError) {
218+
} else if (transactionsState is TransactionsStateError) {
233219
return Center(
234220
child: Text(transactionsState.message),
235221
);
236-
} else if (transactionsState
237-
is TransactionsStateLoaded) {
222+
} else if (transactionsState is TransactionsStateLoaded) {
238223
if (transactionsState.transactions.isEmpty) {
239224
return Center(
240225
child: Column(
@@ -263,10 +248,9 @@ class _DashboardPageState extends State<DashboardPage> {
263248
}
264249
return ListView.separated(
265250
padding: EdgeInsets.zero,
266-
itemCount:
267-
transactionsState.transactions.length <= 30
268-
? transactionsState.transactions.length
269-
: 30,
251+
itemCount: transactionsState.transactions.length <= 30
252+
? transactionsState.transactions.length
253+
: 30,
270254
itemBuilder: (context, index) {
271255
var item = transactionsState.transactions[index];
272256
return Row(
@@ -275,26 +259,25 @@ class _DashboardPageState extends State<DashboardPage> {
275259
Container(
276260
padding: const EdgeInsets.all(10),
277261
decoration: BoxDecoration(
278-
color: Theme.of(context)
279-
.colorScheme
280-
.surfaceBright,
262+
color: Theme.of(context).colorScheme.surfaceBright,
281263
borderRadius:
282-
BorderRadius.circular(10)),
264+
BorderRadius.circular(10)),
283265
child: Icon(
284266
getCategoryIcon(
285267
item.category,
286268
defaultIcon:
287-
item.type == TransactionType.expense
288-
? Icons.payments_rounded
289-
: Icons.file_download_rounded,
269+
item.type ==
270+
TransactionType.expense
271+
? Icons.payments_rounded
272+
: Icons.file_download_rounded,
290273
),
291274
size: 25,
292275
),
293276
),
294277
Expanded(
295278
child: Column(
296279
crossAxisAlignment:
297-
CrossAxisAlignment.start,
280+
CrossAxisAlignment.start,
298281
children: [
299282
Text(item.name),
300283
Text(
@@ -306,17 +289,17 @@ class _DashboardPageState extends State<DashboardPage> {
306289
],
307290
),
308291
),
309-
if (item.type == TransactionType.expense)
292+
if (item.type ==
293+
TransactionType.expense)
310294
Text(
311295
"-${formatMoneyWithContext(context, item.amount)}",
312296
style: TextStyle(
313-
color: Theme.of(context)
314-
.colorScheme
315-
.primary,
297+
color: Theme.of(context).colorScheme.primary,
316298
fontSize: 17,
317299
fontWeight: FontWeight.bold),
318300
)
319-
else if (item.type == TransactionType.income)
301+
else if (item.type ==
302+
TransactionType.income)
320303
Text(
321304
"+${formatMoneyWithContext(context, item.amount)}",
322305
style: const TextStyle(
@@ -330,10 +313,7 @@ class _DashboardPageState extends State<DashboardPage> {
330313
separatorBuilder:
331314
(BuildContext context, int index) {
332315
return Divider(
333-
color: Theme.of(context)
334-
.colorScheme
335-
.surfaceBright,
336-
height: 20);
316+
color: Theme.of(context).colorScheme.surfaceBright, height: 20);
337317
},
338318
);
339319
}
@@ -347,7 +327,7 @@ class _DashboardPageState extends State<DashboardPage> {
347327
)
348328
],
349329
);
350-
});
351-
});
330+
}
331+
);
352332
}
353333
}

0 commit comments

Comments
 (0)