Skip to content

Commit 5d2c27a

Browse files
committed
feat: Refactor dashboard tiles to use a new DashboardTile widget for consistency and improved maintainability
1 parent 10c50b6 commit 5d2c27a

File tree

10 files changed

+546
-474
lines changed

10 files changed

+546
-474
lines changed

client/lib/devices/borneo/lyfi/views/dashboard/dashboard_acclimation_tile.dart

Lines changed: 74 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import 'package:flutter/material.dart';
22
import 'package:flutter_gettext/flutter_gettext/context_ext.dart';
33
import 'package:provider/provider.dart';
4+
5+
import 'package:borneo_app/features/devices/widgets/dashboard_tile.dart';
6+
47
import '../../view_models/lyfi_view_model.dart';
58
import '../acclimation_screen.dart';
69
import 'package:borneo_kernel/drivers/borneo/lyfi/models.dart';
@@ -63,95 +66,82 @@ class DashboardAcclimationTile extends StatelessWidget {
6366
}
6467
}
6568

66-
return AspectRatio(
67-
aspectRatio: 2.0,
68-
child: Container(
69-
decoration: BoxDecoration(color: bgColor, borderRadius: BorderRadius.circular(16)),
70-
child: Material(
71-
color: Colors.transparent,
72-
child: InkWell(
73-
borderRadius: BorderRadius.circular(16),
74-
onTap: props.isOnline && props.isOn
75-
? () async {
76-
if (context.mounted) {
77-
final vm = context.read<LyfiViewModel>();
78-
final route = MaterialPageRoute(
79-
builder: (context) => AcclimationScreen(deviceID: vm.deviceID),
80-
);
81-
await Navigator.push(context, route);
82-
}
83-
}
84-
: null,
85-
child: Padding(
86-
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
87-
child: Stack(
88-
children: [
89-
Row(
90-
crossAxisAlignment: CrossAxisAlignment.center,
91-
children: [
92-
AnimatedSwitcher(
93-
duration: const Duration(milliseconds: 300),
94-
transitionBuilder: (child, animation) => FadeTransition(opacity: animation, child: child),
95-
child: isActive
96-
? SizedBox(
97-
key: const ValueKey('active'),
98-
width: 32,
99-
height: 32,
100-
child: Padding(
101-
padding: EdgeInsets.all(4),
102-
child: CircularProgressIndicator(
103-
strokeAlign: 1,
104-
strokeWidth: 2,
105-
value: progress,
106-
backgroundColor: theme.colorScheme.shadow,
107-
valueColor: AlwaysStoppedAnimation<Color>(theme.colorScheme.onPrimaryContainer),
108-
),
109-
),
110-
)
111-
: Container(
112-
key: const ValueKey('inactive'),
113-
alignment: Alignment.center,
114-
child: Icon(Icons.calendar_month_outlined, size: 32, color: effectiveIconColor),
115-
),
116-
),
117-
const SizedBox(width: 8),
118-
Expanded(
119-
child: Column(
120-
mainAxisAlignment: MainAxisAlignment.center,
121-
crossAxisAlignment: CrossAxisAlignment.start,
122-
children: [
123-
Text(
124-
context.translate("Acclimation"),
125-
style: theme.textTheme.titleMedium?.copyWith(color: effectiveFgColor),
126-
),
127-
if (isActive && remainingText.isNotEmpty)
128-
Text(
129-
remainingText,
130-
style: theme.textTheme.bodySmall?.copyWith(
131-
color: effectiveFgColor,
132-
fontFeatures: [FontFeature.tabularFigures()],
133-
),
134-
),
135-
],
69+
return DashboardTile(
70+
backgroundColor: bgColor,
71+
disabled: !props.isOnline || !props.isOn,
72+
onPressed: props.isOnline && props.isOn
73+
? () async {
74+
if (context.mounted) {
75+
final vm = context.read<LyfiViewModel>();
76+
final route = MaterialPageRoute(builder: (context) => AcclimationScreen(deviceID: vm.deviceID));
77+
await Navigator.push(context, route);
78+
}
79+
}
80+
: null,
81+
child: Stack(
82+
children: [
83+
Row(
84+
crossAxisAlignment: CrossAxisAlignment.center,
85+
children: [
86+
AnimatedSwitcher(
87+
duration: const Duration(milliseconds: 300),
88+
transitionBuilder: (child, animation) => FadeTransition(opacity: animation, child: child),
89+
child: isActive
90+
? SizedBox(
91+
key: const ValueKey('active'),
92+
width: 32,
93+
height: 32,
94+
child: Padding(
95+
padding: EdgeInsets.all(4),
96+
child: CircularProgressIndicator(
97+
strokeAlign: 1,
98+
strokeWidth: 2,
99+
value: progress,
100+
backgroundColor: theme.colorScheme.shadow,
101+
valueColor: AlwaysStoppedAnimation<Color>(theme.colorScheme.onPrimaryContainer),
102+
),
136103
),
104+
)
105+
: Container(
106+
key: const ValueKey('inactive'),
107+
alignment: Alignment.center,
108+
child: Icon(Icons.calendar_month_outlined, size: 32, color: effectiveIconColor),
137109
),
138-
],
139-
),
140-
if (isActive)
141-
Positioned(
142-
right: -16,
143-
bottom: -16,
144-
child: Icon(
145-
Icons.calendar_month_outlined,
146-
size: 64,
147-
color: theme.colorScheme.onSurface.withValues(alpha: 0.15),
148-
),
110+
),
111+
const SizedBox(width: 8),
112+
Expanded(
113+
child: Column(
114+
mainAxisAlignment: MainAxisAlignment.center,
115+
crossAxisAlignment: CrossAxisAlignment.start,
116+
children: [
117+
Text(
118+
context.translate("Acclimation"),
119+
style: theme.textTheme.titleMedium?.copyWith(color: effectiveFgColor),
149120
),
150-
],
121+
if (isActive && remainingText.isNotEmpty)
122+
Text(
123+
remainingText,
124+
style: theme.textTheme.bodySmall?.copyWith(
125+
color: effectiveFgColor,
126+
fontFeatures: [FontFeature.tabularFigures()],
127+
),
128+
),
129+
],
130+
),
151131
),
152-
),
132+
],
153133
),
154-
),
134+
if (isActive)
135+
Positioned(
136+
right: -16,
137+
bottom: -16,
138+
child: Icon(
139+
Icons.calendar_month_outlined,
140+
size: 64,
141+
color: theme.colorScheme.onSurface.withValues(alpha: 0.15),
142+
),
143+
),
144+
],
155145
),
156146
);
157147
},

client/lib/devices/borneo/lyfi/views/dashboard/dashboard_dimming_tile.dart

Lines changed: 59 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import 'package:flutter/material.dart';
22
import 'package:flutter_gettext/flutter_gettext/context_ext.dart';
33
import 'package:persistent_bottom_nav_bar/persistent_bottom_nav_bar.dart';
44
import 'package:provider/provider.dart';
5+
6+
import 'package:borneo_app/features/devices/widgets/dashboard_tile.dart';
7+
58
import '../../view_models/lyfi_view_model.dart';
69
import '../dimming_screen.dart';
710
import 'package:borneo_kernel/drivers/borneo/lyfi/models.dart';
@@ -22,79 +25,65 @@ class DashboardDimmingTile extends StatelessWidget {
2225
final Color effectiveFgColor = isDisabled ? fgColor.withValues(alpha: disabledAlpha) : fgColor;
2326
final Color iconColor = theme.colorScheme.primary;
2427
final Color effectiveIconColor = isDisabled ? iconColor.withValues(alpha: disabledAlpha) : iconColor;
25-
return AspectRatio(
26-
aspectRatio: 2.0,
27-
child: Container(
28-
decoration: BoxDecoration(color: bgColor, borderRadius: BorderRadius.circular(16)),
29-
child: Material(
30-
color: Colors.transparent,
31-
child: InkWell(
32-
borderRadius: BorderRadius.circular(16),
33-
onTap: (canUnlock && context.read<LyfiViewModel>().isOnline)
34-
? () async {
35-
final vm = context.read<LyfiViewModel>();
36-
// Request entering dimming (unlock) then wait for readiness event-driven
37-
await vm.toggleLock(false);
38-
await vm.onDimmingReady();
28+
return DashboardTile(
29+
backgroundColor: bgColor,
30+
disabled: !(canUnlock && context.read<LyfiViewModel>().isOnline),
31+
onPressed: (canUnlock && context.read<LyfiViewModel>().isOnline)
32+
? () async {
33+
final vm = context.read<LyfiViewModel>();
34+
// Request entering dimming (unlock) then wait for readiness event-driven
35+
await vm.toggleLock(false);
36+
await vm.onDimmingReady();
3937

40-
if (context.mounted) {
41-
await PersistentNavBarNavigator.pushNewScreen(
42-
context,
43-
screen: ChangeNotifierProvider.value(value: vm, child: const DimmingScreen()),
44-
withNavBar: false,
45-
pageTransitionAnimation: PageTransitionAnimation.slideRight,
46-
);
47-
}
48-
}
49-
: null,
50-
child: Padding(
51-
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
52-
child: Row(
53-
crossAxisAlignment: CrossAxisAlignment.center,
54-
children: [
55-
Selector<LyfiViewModel, LyfiMode>(
56-
selector: (_, vm) => vm.mode,
57-
builder: (context, mode, _) {
58-
final modeIcon = switch (mode) {
59-
LyfiMode.manual => Icons.bar_chart_outlined,
60-
LyfiMode.scheduled => Icons.alarm_outlined,
61-
LyfiMode.sun => Icons.wb_sunny_outlined,
62-
};
63-
return Icon(modeIcon, size: 32, color: effectiveIconColor);
64-
},
65-
),
66-
const SizedBox(width: 8),
67-
Expanded(
68-
child: Column(
69-
mainAxisAlignment: MainAxisAlignment.center,
70-
crossAxisAlignment: CrossAxisAlignment.start,
71-
children: [
72-
Text(
73-
context.translate('Dimming'),
74-
style: theme.textTheme.titleMedium?.copyWith(color: effectiveFgColor),
75-
),
76-
Selector<LyfiViewModel, LyfiMode>(
77-
selector: (_, vm) => vm.mode,
78-
builder: (context, mode, _) {
79-
final modeText = switch (mode) {
80-
LyfiMode.manual => context.translate('Manual'),
81-
LyfiMode.scheduled => context.translate('Scheduled'),
82-
LyfiMode.sun => context.translate('Sun Simulation'),
83-
};
84-
return Text(
85-
modeText,
86-
style: theme.textTheme.bodySmall?.copyWith(color: effectiveFgColor),
87-
);
88-
},
89-
),
90-
],
91-
),
92-
),
93-
],
94-
),
38+
if (context.mounted) {
39+
await PersistentNavBarNavigator.pushNewScreen(
40+
context,
41+
screen: ChangeNotifierProvider.value(value: vm, child: const DimmingScreen()),
42+
withNavBar: false,
43+
pageTransitionAnimation: PageTransitionAnimation.slideRight,
44+
);
45+
}
46+
}
47+
: null,
48+
child: Row(
49+
crossAxisAlignment: CrossAxisAlignment.center,
50+
children: [
51+
Selector<LyfiViewModel, LyfiMode>(
52+
selector: (_, vm) => vm.mode,
53+
builder: (context, mode, _) {
54+
final modeIcon = switch (mode) {
55+
LyfiMode.manual => Icons.bar_chart_outlined,
56+
LyfiMode.scheduled => Icons.alarm_outlined,
57+
LyfiMode.sun => Icons.wb_sunny_outlined,
58+
};
59+
return Icon(modeIcon, size: 32, color: effectiveIconColor);
60+
},
61+
),
62+
const SizedBox(width: 8),
63+
Expanded(
64+
child: Column(
65+
mainAxisAlignment: MainAxisAlignment.center,
66+
crossAxisAlignment: CrossAxisAlignment.start,
67+
children: [
68+
Text(
69+
context.translate('Dimming'),
70+
style: theme.textTheme.titleMedium?.copyWith(color: effectiveFgColor),
71+
),
72+
Selector<LyfiViewModel, LyfiMode>(
73+
selector: (_, vm) => vm.mode,
74+
builder: (context, mode, _) {
75+
final modeText = switch (mode) {
76+
LyfiMode.manual => context.translate('Manual'),
77+
LyfiMode.scheduled => context.translate('Scheduled'),
78+
LyfiMode.sun => context.translate('Sun Simulation'),
79+
};
80+
return Text(modeText, style: theme.textTheme.bodySmall?.copyWith(color: effectiveFgColor));
81+
},
82+
),
83+
],
9584
),
9685
),
97-
),
86+
],
9887
),
9988
);
10089
},

0 commit comments

Comments
 (0)