diff options
-rw-r--r-- | lib/homescreen.dart | 2 | ||||
-rw-r--r-- | lib/page_dashboard.dart | 167 | ||||
-rw-r--r-- | lib/page_home.dart | 107 | ||||
-rw-r--r-- | lib/page_hvac.dart | 316 |
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, + ), + ), + ); + } +} |