aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/homescreen.dart2
-rw-r--r--lib/page_dashboard.dart167
-rw-r--r--lib/page_home.dart107
-rw-r--r--lib/page_hvac.dart316
4 files changed, 333 insertions, 259 deletions
diff --git a/lib/homescreen.dart b/lib/homescreen.dart
index 3c1934f..1591169 100644
--- a/lib/homescreen.dart
+++ b/lib/homescreen.dart
@@ -36,7 +36,7 @@ class _HomescreenState extends State<Homescreen> with TickerProviderStateMixin {
case PageIndex.dashboard:
return DashboardPage(key: ValueKey(selectedIndex));
case PageIndex.hvac:
- return HVACPageContainer(key: ValueKey(selectedIndex));
+ return HVACPage(key: ValueKey(selectedIndex));
case PageIndex.media:
return MediaPage(key: ValueKey(selectedIndex));
case PageIndex.demo3d:
diff --git a/lib/page_dashboard.dart b/lib/page_dashboard.dart
index 09d1eef..c354415 100644
--- a/lib/page_dashboard.dart
+++ b/lib/page_dashboard.dart
@@ -4,6 +4,7 @@ import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_homescreen/layout_size_helper.dart';
+// The Dashboard page.
class DashboardPage extends StatefulWidget {
DashboardPage({Key? key}) : super(key: key);
@@ -26,13 +27,15 @@ class _DashboardPageState extends State<DashboardPage> {
Duration(milliseconds: 10),
(Timer timer) {
setState(() {
- double now = DateTime.now().millisecondsSinceEpoch / 1000;
+ double now = DateTime.now().millisecondsSinceEpoch / 2000;
speed = 50 + 40 * sin(now);
rpm = 0.5 + sin(now) / 3.0;
fuel = 0.6 + cos(now) / 4.0;
});
},
);
+ // Animate the values for the demo.
+ // Eventually, we will get the state of the car from the API.
super.initState();
}
@@ -53,89 +56,69 @@ class _DashboardPageState extends State<DashboardPage> {
colors: [Colors.teal.shade900, Colors.grey.shade900])),
constraints: BoxConstraints.expand(),
alignment: Alignment.center,
- child: Column(
+ child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
- Text(
- '${speed.floor()} kpm',
- style: Theme.of(context).textTheme.headline2,
+ Column(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: [
+ Container(
+ height: sizeHelper.largeIconSize * 1,
+ width: sizeHelper.largeIconSize * 2,
+ decoration: BoxDecoration(
+ border: Border.all(
+ color: Theme.of(context).primaryColorLight,
+ width: sizeHelper.defaultBorder,
+ ),
+ borderRadius: BorderRadius.all(
+ Radius.circular(sizeHelper.largeIconSize / 2.0))),
+ child: Center(
+ child: Text(
+ '${speed.floor()} kpm',
+ style: Theme.of(context).textTheme.headline2,
+ ),
+ ),
+ ),
+ _RPMWidget(rpm),
+ _FuelWidget(fuel),
+ ],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
- Expanded(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- crossAxisAlignment: CrossAxisAlignment.end,
- children: [
- _TireWidget('Left front tire', 21, CrossAxisAlignment.end),
- SizedBox(
- height: sizeHelper.largeIconSize,
- ),
- _TireWidget('Left rear tire', 23, CrossAxisAlignment.end),
- ],
- ),
+ Column(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ crossAxisAlignment: CrossAxisAlignment.end,
+ children: [
+ _TireWidget('Left front tire', 21, CrossAxisAlignment.end),
+ _TireWidget('Left rear tire', 23, CrossAxisAlignment.end),
+ ],
),
Image.asset(
'images/HMI_Dashboard_Car_720.png',
- width: sizeHelper.largeIconSize,
+ width: 2.0 * sizeHelper.largeIconSize,
fit: BoxFit.contain,
),
- Expanded(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- _TireWidget(
- 'Right front tire', 21, CrossAxisAlignment.start),
- SizedBox(
- height: sizeHelper.largeIconSize,
- ),
- _TireWidget(
- 'Right rear tire', 23, CrossAxisAlignment.start),
- ],
- ),
+ Column(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ _TireWidget('Right front tire', 21, CrossAxisAlignment.start),
+ _TireWidget('Right rear tire', 23, CrossAxisAlignment.start),
+ ],
),
],
),
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceAround,
- children: [
- _RPMWidget(rpm),
- _FuelWidget(fuel),
- ],
- )
],
),
);
}
}
-class _TireWidget extends StatelessWidget {
- String label;
- int value;
- CrossAxisAlignment crossAlign;
-
- _TireWidget(this.label, this.value, this.crossAlign);
-
- @override
- Widget build(BuildContext context) {
- var sizeHelper = LayoutSizeHelper(context);
- return Column(crossAxisAlignment: crossAlign, children: [
- Text(
- label,
- style: Theme.of(context).textTheme.caption,
- ),
- Text(
- '$value PSI',
- style: Theme.of(context).textTheme.headline5,
- )
- ]);
- }
-}
-
+// The RPM indicator.
class _RPMWidget extends StatelessWidget {
- double rpm;
+ final double rpm;
_RPMWidget(this.rpm);
@@ -150,13 +133,16 @@ class _RPMWidget extends StatelessWidget {
style: Theme.of(context).textTheme.headline4,
),
Container(
- height: sizeHelper.largeIconSize,
- width: sizeHelper.largeIconSize,
+ height: sizeHelper.largeIconSize * 1.5,
+ width: sizeHelper.largeIconSize * 1.5,
child: RotatedBox(
quarterTurns: 2,
child: CircularProgressIndicator(
value: rpm,
- strokeWidth: sizeHelper.largeIconSize / 4.0,
+ color: HSLColor.fromColor(Colors.redAccent)
+ .withSaturation(rpm)
+ .toColor(),
+ strokeWidth: sizeHelper.largeIconSize / 2.0,
semanticsLabel: 'RPM indicator',
),
),
@@ -166,8 +152,9 @@ class _RPMWidget extends StatelessWidget {
}
}
+// The fuel indicator.
class _FuelWidget extends StatelessWidget {
- double fuel;
+ final double fuel;
_FuelWidget(this.fuel);
@@ -176,16 +163,26 @@ class _FuelWidget extends StatelessWidget {
var sizeHelper = LayoutSizeHelper(context);
return Row(
children: [
- Text(
- 'Fuel',
- style: Theme.of(context).textTheme.headline4,
+ Container(
+ height: sizeHelper.largeIconSize / 4.0,
+ width: sizeHelper.largeIconSize / 2.0,
+ child: Center(
+ child: Text(
+ 'Fuel',
+ style: Theme.of(context).textTheme.headline4,
+ ),
+ ),
),
Container(
height: sizeHelper.largeIconSize / 4.0,
- width: sizeHelper.largeIconSize,
- margin: EdgeInsets.all(sizeHelper.largePadding),
+ width: sizeHelper.largeIconSize * 1.5,
+ margin: EdgeInsets.fromLTRB(
+ 0, sizeHelper.largePadding, 0, sizeHelper.largePadding),
child: LinearProgressIndicator(
value: fuel,
+ color: HSLColor.fromColor(Colors.blueAccent)
+ .withSaturation(fuel)
+ .toColor(),
semanticsLabel: 'RPM indicator',
),
)
@@ -193,3 +190,29 @@ class _FuelWidget extends StatelessWidget {
);
}
}
+
+// The small indicator for the state of each tire.
+class _TireWidget extends StatelessWidget {
+ final String label;
+ final int value;
+ final CrossAxisAlignment crossAlign;
+
+ _TireWidget(this.label, this.value, this.crossAlign);
+
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ crossAxisAlignment: crossAlign,
+ children: [
+ Text(
+ label,
+ style: Theme.of(context).textTheme.headline6,
+ ),
+ Text(
+ '$value PSI',
+ style: Theme.of(context).textTheme.headline4,
+ ),
+ ],
+ );
+ }
+}
diff --git a/lib/page_home.dart b/lib/page_home.dart
index 7b4bc79..1e9a213 100644
--- a/lib/page_home.dart
+++ b/lib/page_home.dart
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_homescreen/layout_size_helper.dart';
+// The Home page.
class HomePage extends StatelessWidget {
final Function(int index) onSetNavigationIndex;
@@ -9,44 +10,92 @@ class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
- var themeHelper = LayoutSizeHelper(context);
+ var sizeHelper = LayoutSizeHelper(context);
return Container(
- color: Colors.lightBlue.shade50,
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ begin: Alignment.topRight,
+ end: Alignment.bottomLeft,
+ colors: [Colors.blueGrey.shade800, Colors.grey.shade900])),
constraints: BoxConstraints.expand(),
alignment: Alignment.center,
child: Wrap(
- spacing: themeHelper.largePadding,
- runSpacing: themeHelper.largePadding,
+ spacing: sizeHelper.largePadding,
+ runSpacing: sizeHelper.largePadding,
children: <Widget>[
- createItem(themeHelper, Icons.drive_eta, 1),
- createItem(themeHelper, Icons.thermostat, 2),
- createItem(themeHelper, Icons.music_note, 3)
+ _HomePageEntry(
+ label: "DASHBOARD",
+ icon: Icons.drive_eta,
+ onPressed: () {
+ onSetNavigationIndex(1);
+ },
+ ),
+ _HomePageEntry(
+ label: "HVAC",
+ icon: Icons.thermostat,
+ onPressed: () {
+ onSetNavigationIndex(2);
+ },
+ ),
+ _HomePageEntry(
+ label: "MEDIA",
+ icon: Icons.music_note,
+ onPressed: () {
+ onSetNavigationIndex(3);
+ },
+ ),
],
));
}
+}
+
+// Each one of the items on the Home page.
+class _HomePageEntry extends StatelessWidget {
+ final String label;
+ final IconData icon;
+ final Null Function() onPressed;
+
+ const _HomePageEntry(
+ {Key? key,
+ required this.label,
+ required this.icon,
+ required this.onPressed})
+ : super(key: key);
- Widget createItem(
- LayoutSizeHelper themeHelper, IconData icon, int tabPosition) {
+ @override
+ Widget build(BuildContext context) {
+ var sizeHelper = LayoutSizeHelper(context);
return Padding(
- padding: const EdgeInsets.symmetric(vertical: 8.0),
- child: OutlinedButton(
- style: OutlinedButton.styleFrom(
- shape: CircleBorder(),
- padding: EdgeInsets.all(themeHelper.largePadding),
- primary: Colors.lightBlue.shade400,
- side: BorderSide(
- width: themeHelper.defaultBorder,
- color: Colors.lightBlue.shade400),
- ),
- onPressed: () {
- onSetNavigationIndex(tabPosition);
- },
- child: Icon(
- icon,
- color: Colors.lightBlue.shade800,
- size: themeHelper.largeIconSize,
- ),
- ),
- );
+ padding: const EdgeInsets.symmetric(vertical: 8.0),
+ child: Column(
+ children: [
+ ElevatedButton(
+ style: ElevatedButton.styleFrom(
+ shape: CircleBorder(),
+ padding: EdgeInsets.all(sizeHelper.largePadding),
+ primary: Colors.lightBlue.shade800,
+ side: BorderSide(
+ width: sizeHelper.defaultBorder,
+ color: Colors.lightBlue.shade200),
+ ),
+ onPressed: onPressed,
+ child: Icon(
+ icon,
+ color: Colors.lightBlue.shade50,
+ size: sizeHelper.largeIconSize,
+ ),
+ ),
+ Padding(
+ padding: EdgeInsets.all(sizeHelper.defaultPadding),
+ child: Text(
+ label,
+ style: DefaultTextStyle.of(context).style.copyWith(
+ fontSize: sizeHelper.baseFontSize,
+ color: Colors.lightBlue.shade100,
+ ),
+ ),
+ ),
+ ],
+ ));
}
}
diff --git a/lib/page_hvac.dart b/lib/page_hvac.dart
index 2b5b825..488d140 100644
--- a/lib/page_hvac.dart
+++ b/lib/page_hvac.dart
@@ -2,91 +2,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_homescreen/layout_size_helper.dart';
import 'package:numberpicker/numberpicker.dart';
-class HVACPageContainer extends StatelessWidget {
- const HVACPageContainer({Key? key}) : super(key: key);
- @override
- Widget build(BuildContext context) {
- return Container(
- constraints: BoxConstraints.expand(),
- alignment: Alignment.center,
- child: const HVACPage(title: 'AGL - Flutter HVAC'),
- );
- }
-}
-
-class _TemperatureSelector extends StatefulWidget {
- _TemperatureSelector({Key? key}) : super(key: key);
-
- @override
- _TemperatureSelectorState createState() => _TemperatureSelectorState();
-}
-
-class _TemperatureSelectorState extends State<_TemperatureSelector> {
- int _currentValue = 22; // INIT FROM AGLJS wrapper
-
- @override
- Widget build(BuildContext context) {
- var sizeHelper = LayoutSizeHelper(context);
- return Column(
- children: <Widget>[
- NumberPicker(
- value: _currentValue,
- minValue: 18,
- maxValue: 25,
- onChanged: (value) => setState(() => _currentValue = value),
- textStyle: DefaultTextStyle.of(context).style.copyWith(
- color: Colors.teal.shade200,
- fontSize: sizeHelper.baseFontSize,
- ),
- selectedTextStyle: DefaultTextStyle.of(context).style.copyWith(
- fontSize: sizeHelper.baseFontSize * 1.5,
- ),
- itemHeight: sizeHelper.baseFontSize * 3,
- itemWidth: sizeHelper.baseFontSize * 6,
- ),
- ],
- );
- }
-}
-
-/// This is the stateful widget that the main application instantiates.
-class HVACFanSpeed extends StatefulWidget {
- const HVACFanSpeed({Key? key}) : super(key: key);
- @override
- State<HVACFanSpeed> createState() => _HVACFanSpeedState();
-}
-
-/// This is the private State class that goes with MyStatefulWidget.
-class _HVACFanSpeedState extends State<HVACFanSpeed> {
- double _currentSliderValue = 20;
-
- @override
- Widget build(BuildContext context) {
- return SliderTheme(
- data: SliderThemeData(
- thumbColor: Colors.greenAccent.shade700,
- activeTrackColor: Colors.greenAccent.shade700,
- inactiveTrackColor: Colors.blueGrey.shade200,
- ),
- child: Slider(
- value: _currentSliderValue,
- min: 0,
- max: 300,
- label: _currentSliderValue.round().toString(),
- onChanged: (double value) {
- setState(() {
- _currentSliderValue = value;
- });
- },
- ),
- );
- }
-}
-
+// The page for heating, ventilation, and air conditioning.
class HVACPage extends StatefulWidget {
- const HVACPage({Key? key, required this.title}) : super(key: key);
-
- final String title;
+ const HVACPage({Key? key}) : super(key: key);
@override
State<HVACPage> createState() => _HVACPageState();
@@ -99,25 +17,18 @@ String rightChairOff = 'images/HMI_HVAC_Right_Chair_OFF.png';
String circulationActive = 'images/HMI_HVAC_Circulation_Active.png';
String circulationInactive = 'images/HMI_HVAC_Circulation_Inactive.png';
-// Get from API
-bool leftChairSelected = true;
-bool rightChairSelected = true;
-bool acSelected = true;
-bool autoSelected = true;
-bool circulationSelected = true;
-
class _HVACPageState extends State<HVACPage> {
- final double fanSpeed = 20;
+// Get from API
+ bool leftChairSelected = true;
+ bool rightChairSelected = true;
+ bool acSelected = true;
+ bool autoSelected = false;
+ bool circulationSelected = false;
+ double fanSpeed = 20;
@override
Widget build(BuildContext context) {
var sizeHelper = LayoutSizeHelper(context);
- TextStyle buttonTextStyle = DefaultTextStyle.of(context).style.copyWith(
- fontSize: sizeHelper.baseFontSize,
- );
- TextStyle unselectedButtonTextStyle = buttonTextStyle.copyWith(
- color: Colors.grey,
- );
Widget fanSpeedControl = Container(
padding: EdgeInsets.symmetric(
@@ -126,7 +37,16 @@ class _HVACPageState extends State<HVACPage> {
),
child: Row(
children: [
- Expanded(flex: 4, child: const HVACFanSpeed()),
+ Expanded(
+ flex: 4,
+ child: HVACFanSpeed(
+ fanSpeed: fanSpeed,
+ onUpdateFanSpeed: (double newFanSpeed) {
+ setState(() {
+ fanSpeed = newFanSpeed;
+ });
+ },
+ )),
SizedBox(width: sizeHelper.defaultPadding),
Expanded(
flex: 1,
@@ -186,72 +106,34 @@ class _HVACPageState extends State<HVACPage> {
padding: EdgeInsets.all(sizeHelper.defaultPadding),
child: Column(
children: [
- Container(
- width: sizeHelper.defaultButtonWidth,
- height: sizeHelper.defaultButtonHeight,
- margin: EdgeInsets.all(sizeHelper.defaultPadding),
- decoration: BoxDecoration(
- border: Border.all(
- color: acSelected ? Colors.green : Colors.grey,
- ),
- ),
- child: OutlinedButton(
+ _HVACToggleButton(
+ label: 'A/C',
+ isSelected: acSelected,
onPressed: () {
setState(() {
acSelected = !acSelected;
});
- },
- child: Text(
- "A / C",
- style: acSelected ? buttonTextStyle : unselectedButtonTextStyle,
- ),
- ),
- ),
- Container(
- width: sizeHelper.defaultButtonWidth,
- height: sizeHelper.defaultButtonHeight,
- margin: EdgeInsets.all(sizeHelper.defaultPadding),
- decoration: BoxDecoration(
- border: Border.all(
- color: autoSelected ? Colors.green : Colors.grey,
- ),
- ),
- child: OutlinedButton(
+ }),
+ _HVACToggleButton(
+ label: 'Auto',
+ isSelected: autoSelected,
onPressed: () {
setState(() {
autoSelected = !autoSelected;
});
- },
- child: Text(
- "Auto",
- style:
- autoSelected ? buttonTextStyle : unselectedButtonTextStyle,
- ),
- ),
- ),
- Container(
- width: sizeHelper.defaultButtonWidth,
- height: sizeHelper.defaultButtonHeight,
- margin: EdgeInsets.all(sizeHelper.defaultPadding),
- decoration: BoxDecoration(
- border: Border.all(
- color: circulationSelected ? Colors.green : Colors.grey,
- ),
- ),
- child: OutlinedButton(
- onPressed: () {
- setState(() {
- circulationSelected = !circulationSelected;
- });
- },
- child: Image.asset(
- circulationSelected
- ? circulationActive
- : circulationInactive,
- width: sizeHelper.defaultIconSize,
- height: sizeHelper.defaultIconSize,
- fit: BoxFit.contain)),
- ),
+ }),
+ _HVACToggleButton(
+ child: Image.asset(
+ circulationSelected ? circulationActive : circulationInactive,
+ width: sizeHelper.defaultIconSize,
+ height: sizeHelper.defaultIconSize,
+ fit: BoxFit.contain),
+ isSelected: circulationSelected,
+ onPressed: () {
+ setState(() {
+ circulationSelected = !circulationSelected;
+ });
+ }),
],
),
);
@@ -302,3 +184,123 @@ class _HVACPageState extends State<HVACPage> {
));
}
}
+
+// The temperature selector.
+class _TemperatureSelector extends StatefulWidget {
+ _TemperatureSelector({Key? key}) : super(key: key);
+
+ @override
+ _TemperatureSelectorState createState() => _TemperatureSelectorState();
+}
+
+class _TemperatureSelectorState extends State<_TemperatureSelector> {
+ int _currentValue = 22; // INIT FROM AGLJS wrapper
+
+ @override
+ Widget build(BuildContext context) {
+ var sizeHelper = LayoutSizeHelper(context);
+ return Column(
+ children: <Widget>[
+ NumberPicker(
+ value: _currentValue,
+ minValue: 18,
+ maxValue: 25,
+ onChanged: (value) => setState(() => _currentValue = value),
+ textStyle: DefaultTextStyle.of(context).style.copyWith(
+ color: Colors.teal.shade200,
+ fontSize: sizeHelper.baseFontSize,
+ ),
+ selectedTextStyle: DefaultTextStyle.of(context).style.copyWith(
+ fontSize: sizeHelper.baseFontSize * 1.5,
+ ),
+ itemHeight: sizeHelper.baseFontSize * 3,
+ itemWidth: sizeHelper.baseFontSize * 6,
+ ),
+ ],
+ );
+ }
+}
+
+/// The fan speed control.
+class HVACFanSpeed extends StatelessWidget {
+ final double fanSpeed;
+ final Null Function(double) onUpdateFanSpeed;
+
+ const HVACFanSpeed(
+ {Key? key, required this.fanSpeed, required this.onUpdateFanSpeed})
+ : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return SliderTheme(
+ data: SliderThemeData(
+ thumbColor: Colors.greenAccent.shade700,
+ activeTrackColor: Colors.greenAccent.shade700,
+ inactiveTrackColor: Colors.blueGrey.shade200,
+ ),
+ child: Slider(
+ value: fanSpeed,
+ min: 0,
+ max: 300,
+ label: fanSpeed.round().toString(),
+ onChanged: (double value) {
+ onUpdateFanSpeed(value);
+ },
+ ),
+ );
+ }
+}
+
+// Each one of the toggle buttons in the UI.
+class _HVACToggleButton extends StatelessWidget {
+ final String? label;
+ final Widget? child;
+ final bool isSelected;
+ final Null Function() onPressed;
+
+ _HVACToggleButton(
+ {Key? key,
+ this.label,
+ this.child,
+ required this.isSelected,
+ required this.onPressed})
+ : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ var sizeHelper = LayoutSizeHelper(context);
+ TextStyle buttonTextStyle = DefaultTextStyle.of(context).style.copyWith(
+ fontSize: sizeHelper.baseFontSize,
+ fontWeight: FontWeight.bold,
+ );
+ TextStyle unselectedButtonTextStyle = buttonTextStyle.copyWith(
+ color: Colors.grey,
+ fontWeight: FontWeight.normal,
+ );
+
+ return Container(
+ width: sizeHelper.defaultButtonWidth,
+ height: sizeHelper.defaultButtonHeight,
+ margin: EdgeInsets.all(sizeHelper.defaultPadding),
+ child: OutlinedButton(
+ onPressed: onPressed,
+ style: OutlinedButton.styleFrom(
+ shape: RoundedRectangleBorder(
+ borderRadius:
+ BorderRadius.circular(sizeHelper.defaultButtonHeight / 4.0),
+ ),
+ side: BorderSide(
+ width: sizeHelper.defaultBorder,
+ color: isSelected ? Colors.green : Colors.grey,
+ style: BorderStyle.solid,
+ ),
+ ),
+ child: child ??
+ Text(
+ label ?? '',
+ style: isSelected ? buttonTextStyle : unselectedButtonTextStyle,
+ ),
+ ),
+ );
+ }
+}