From b8ba3c59e9400ba207493d93e3f623401eaa8d6a Mon Sep 17 00:00:00 2001 From: Yegor Vialov Date: Sat, 4 Apr 2020 12:00:15 +0000 Subject: [PATCH] WIP Themes: climate controls and entity name --- lib/cards/widgets/card_header.widget.dart | 2 +- .../entity_button_card_body.widget.dart | 3 +- lib/cards/widgets/gauge_card_body.dart | 4 +- .../widgets/glance_card_entity_container.dart | 10 ++-- lib/cards/widgets/light_card_body.dart | 4 +- .../climate/widgets/climate_controls.dart | 52 ++++++++----------- .../climate/widgets/climate_state.widget.dart | 13 ++--- .../climate/widgets/mode_selector.dart | 13 +---- lib/entities/climate/widgets/mode_swicth.dart | 10 ++-- .../widgets/temperature_control_widget.dart | 13 ++--- lib/entities/entity_name.widget.dart | 15 +++--- lib/main.dart | 10 ++-- lib/managers/theme_manager.dart | 33 ++++++++++++ lib/pages/integration_settings.page.dart | 15 +++--- 14 files changed, 104 insertions(+), 93 deletions(-) create mode 100644 lib/managers/theme_manager.dart diff --git a/lib/cards/widgets/card_header.widget.dart b/lib/cards/widgets/card_header.widget.dart index f811a40..6ecbf14 100644 --- a/lib/cards/widgets/card_header.widget.dart +++ b/lib/cards/widgets/card_header.widget.dart @@ -18,7 +18,7 @@ class CardHeader extends StatelessWidget { title: Text("$name", textAlign: TextAlign.left, overflow: TextOverflow.ellipsis, - style: new TextStyle(fontWeight: FontWeight.bold, fontSize: Sizes.largeFontSize)), + style: Theme.of(context).textTheme.headline), ); } else { result = new Container(width: 0.0, height: 0.0); diff --git a/lib/cards/widgets/entity_button_card_body.widget.dart b/lib/cards/widgets/entity_button_card_body.widget.dart index 5e8d635..f845230 100644 --- a/lib/cards/widgets/entity_button_card_body.widget.dart +++ b/lib/cards/widgets/entity_button_card_body.widget.dart @@ -48,8 +48,7 @@ class EntityButtonCardBody extends StatelessWidget { textOverflow: TextOverflow.ellipsis, maxLines: 3, wordsWrap: true, - textAlign: TextAlign.center, - fontSize: Sizes.nameFontSize, + textAlign: TextAlign.center ); } return Container(width: 0, height: 0); diff --git a/lib/cards/widgets/gauge_card_body.dart b/lib/cards/widgets/gauge_card_body.dart index a04a5c5..447af78 100644 --- a/lib/cards/widgets/gauge_card_body.dart +++ b/lib/cards/widgets/gauge_card_body.dart @@ -126,7 +126,9 @@ class _GaugeCardBodyState extends State { return Padding( padding: EdgeInsets.only(bottom: fontSize), child: EntityName( - fontSize: fontSize, + textStyle: Theme.of(context).textTheme.body1.copyWith( + fontSize: fontSize + ), maxLines: 1, padding: EdgeInsets.all(0.0), textAlign: TextAlign.center, diff --git a/lib/cards/widgets/glance_card_entity_container.dart b/lib/cards/widgets/glance_card_entity_container.dart index 12fe0aa..42a786f 100644 --- a/lib/cards/widgets/glance_card_entity_container.dart +++ b/lib/cards/widgets/glance_card_entity_container.dart @@ -6,7 +6,6 @@ class GlanceCardEntityContainer extends StatelessWidget { final bool showState; final bool nameInTheBottom; final double iconSize; - final double nameFontSize; final bool wordsWrapInName; GlanceCardEntityContainer({ @@ -15,7 +14,6 @@ class GlanceCardEntityContainer extends StatelessWidget { @required this.showState, this.nameInTheBottom: false, this.iconSize: Sizes.iconSize, - this.nameFontSize: Sizes.smallFontSize, this.wordsWrapInName: false }) : super(key: key); @@ -31,7 +29,7 @@ class GlanceCardEntityContainer extends StatelessWidget { List result = []; if (!nameInTheBottom) { if (showName) { - result.add(_buildName()); + result.add(_buildName(context)); } } else { if (showState) { @@ -49,7 +47,7 @@ class GlanceCardEntityContainer extends StatelessWidget { result.add(_buildState()); } } else { - result.add(_buildName()); + result.add(_buildName(context)); } return Center( @@ -65,13 +63,13 @@ class GlanceCardEntityContainer extends StatelessWidget { ); } - Widget _buildName() { + Widget _buildName(BuildContext context) { return EntityName( padding: EdgeInsets.only(bottom: Sizes.rowPadding), textOverflow: TextOverflow.ellipsis, wordsWrap: wordsWrapInName, textAlign: TextAlign.center, - fontSize: nameFontSize, + textStyle: Theme.of(context).textTheme.caption, ); } diff --git a/lib/cards/widgets/light_card_body.dart b/lib/cards/widgets/light_card_body.dart index ceed3ba..675f6f5 100644 --- a/lib/cards/widgets/light_card_body.dart +++ b/lib/cards/widgets/light_card_body.dart @@ -73,7 +73,9 @@ class _LightCardBodyState extends State { return Padding( padding: EdgeInsets.only(bottom: fontSize), child: EntityName( - fontSize: fontSize, + textStyle: Theme.of(context).textTheme.body1.copyWith( + fontSize: fontSize + ), maxLines: 1, padding: EdgeInsets.all(0.0), textAlign: TextAlign.center, diff --git a/lib/entities/climate/widgets/climate_controls.dart b/lib/entities/climate/widgets/climate_controls.dart index 9982f26..f3610c8 100644 --- a/lib/entities/climate/widgets/climate_controls.dart +++ b/lib/entities/climate/widgets/climate_controls.dart @@ -204,20 +204,20 @@ class _ClimateControlWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ //_buildOnOffControl(entity), - _buildTemperatureControls(entity), - _buildTargetTemperatureControls(entity), - _buildHumidityControls(entity), - _buildOperationControl(entity), - _buildFanControl(entity), - _buildSwingControl(entity), - _buildPresetModeControl(entity), - _buildAuxHeatControl(entity) + _buildTemperatureControls(entity, context), + _buildTargetTemperatureControls(entity, context), + _buildHumidityControls(entity, context), + _buildOperationControl(entity, context), + _buildFanControl(entity, context), + _buildSwingControl(entity, context), + _buildPresetModeControl(entity, context), + _buildAuxHeatControl(entity, context) ], ), ); } - Widget _buildPresetModeControl(ClimateEntity entity) { + Widget _buildPresetModeControl(ClimateEntity entity, BuildContext context) { if (entity.supportPresetMode) { return ModeSelectorWidget( options: entity.presetModes, @@ -242,7 +242,7 @@ class _ClimateControlWidgetState extends State { } }*/ - Widget _buildAuxHeatControl(ClimateEntity entity) { + Widget _buildAuxHeatControl(ClimateEntity entity, BuildContext context) { if (entity.supportAuxHeat ) { return ModeSwitchWidget( caption: "Aux heat", @@ -254,7 +254,7 @@ class _ClimateControlWidgetState extends State { } } - Widget _buildOperationControl(ClimateEntity entity) { + Widget _buildOperationControl(ClimateEntity entity, BuildContext context) { if (entity.hvacModes != null) { return ModeSelectorWidget( onChange: (mode) => _setHVACMode(entity, mode), @@ -267,7 +267,7 @@ class _ClimateControlWidgetState extends State { } } - Widget _buildFanControl(ClimateEntity entity) { + Widget _buildFanControl(ClimateEntity entity, BuildContext context) { if (entity.supportFanMode) { return ModeSelectorWidget( options: entity.fanModes, @@ -280,7 +280,7 @@ class _ClimateControlWidgetState extends State { } } - Widget _buildSwingControl(ClimateEntity entity) { + Widget _buildSwingControl(ClimateEntity entity, BuildContext context) { if (entity.supportSwingMode) { return ModeSelectorWidget( onChange: (mode) => _setSwingMode(entity, mode), @@ -293,17 +293,15 @@ class _ClimateControlWidgetState extends State { } } - Widget _buildTemperatureControls(ClimateEntity entity) { + Widget _buildTemperatureControls(ClimateEntity entity, BuildContext context) { if ((entity.supportTargetTemperature) && (entity.temperature != null)) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("Target temperature", style: TextStyle( - fontSize: Sizes.stateFontSize - )), + Text("Target temperature", style: Theme.of(context).textTheme.body1), TemperatureControlWidget( value: _tmpTemperature, - fontColor: _temperaturePending ? Colors.red : Colors.black, + active: _temperaturePending, onDec: () => _temperatureDown(entity), onInc: () => _temperatureUp(entity), ) @@ -314,13 +312,13 @@ class _ClimateControlWidgetState extends State { } } - Widget _buildTargetTemperatureControls(ClimateEntity entity) { + Widget _buildTargetTemperatureControls(ClimateEntity entity, BuildContext context) { List controls = []; if ((entity.supportTargetTemperatureRange) && (entity.targetLow != null)) { controls.addAll([ TemperatureControlWidget( value: _tmpTargetLow, - fontColor: _temperaturePending ? Colors.red : Colors.black, + active: _temperaturePending, onDec: () => _targetLowDown(entity), onInc: () => _targetLowUp(entity), ), @@ -333,7 +331,7 @@ class _ClimateControlWidgetState extends State { controls.add( TemperatureControlWidget( value: _tmpTargetHigh, - fontColor: _temperaturePending ? Colors.red : Colors.black, + active: _temperaturePending, onDec: () => _targetHighDown(entity), onInc: () => _targetHighUp(entity), ) @@ -343,9 +341,7 @@ class _ClimateControlWidgetState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("Target temperature range", style: TextStyle( - fontSize: Sizes.stateFontSize - )), + Text("Target temperature range", style: Theme.of(context).textTheme.body1), Row( children: controls, ) @@ -356,13 +352,13 @@ class _ClimateControlWidgetState extends State { } } - Widget _buildHumidityControls(ClimateEntity entity) { + Widget _buildHumidityControls(ClimateEntity entity, BuildContext context) { List result = []; if (entity.supportTargetHumidity) { result.addAll([ Text( "$_tmpTargetHumidity%", - style: TextStyle(fontSize: Sizes.largeFontSize), + style: Theme.of(context).textTheme.display1, ), Expanded( child: Slider( @@ -387,9 +383,7 @@ class _ClimateControlWidgetState extends State { Padding( padding: EdgeInsets.fromLTRB( 0.0, Sizes.rowPadding, 0.0, Sizes.rowPadding), - child: Text("Target humidity", style: TextStyle( - fontSize: Sizes.stateFontSize - )), + child: Text("Target humidity", style: Theme.of(context).textTheme.body1), ), Row( crossAxisAlignment: CrossAxisAlignment.center, diff --git a/lib/entities/climate/widgets/climate_state.widget.dart b/lib/entities/climate/widgets/climate_state.widget.dart index 4d9c244..74b7aa1 100644 --- a/lib/entities/climate/widgets/climate_state.widget.dart +++ b/lib/entities/climate/widgets/climate_state.widget.dart @@ -33,23 +33,16 @@ class ClimateStateWidget extends StatelessWidget { children: [ Text("$displayState", textAlign: TextAlign.right, - style: new TextStyle( - fontWeight: FontWeight.bold, - fontSize: Sizes.stateFontSize, - )), + style: Theme.of(context).textTheme.body2), Text(" $targetTemp", textAlign: TextAlign.right, - style: new TextStyle( - fontSize: Sizes.stateFontSize, - )) + style: Theme.of(context).textTheme.body1) ], ), entity.currentTemperature != null ? Text("Currently: ${entity.currentTemperature}", textAlign: TextAlign.right, - style: new TextStyle( - fontSize: Sizes.stateFontSize, - color: Colors.black45) + style: Theme.of(context).textTheme.subtitle ) : Container(height: 0.0,) ], diff --git a/lib/entities/climate/widgets/mode_selector.dart b/lib/entities/climate/widgets/mode_selector.dart index 6ff5d77..58bc823 100644 --- a/lib/entities/climate/widgets/mode_selector.dart +++ b/lib/entities/climate/widgets/mode_selector.dart @@ -5,8 +5,6 @@ class ModeSelectorWidget extends StatelessWidget { final String caption; final List options; final String value; - final double captionFontSize; - final double valueFontSize; final onChange; final EdgeInsets padding; @@ -16,8 +14,6 @@ class ModeSelectorWidget extends StatelessWidget { @required this.options, this.value, @required this.onChange, - this.captionFontSize, - this.valueFontSize, this.padding: const EdgeInsets.fromLTRB(Sizes.leftWidgetPadding, Sizes.rowPadding, Sizes.rightWidgetPadding, 0.0), }) : super(key: key); @@ -28,9 +24,7 @@ class ModeSelectorWidget extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("$caption", style: TextStyle( - fontSize: captionFontSize ?? Sizes.stateFontSize - )), + Text("$caption", style: Theme.of(context).textTheme.body1), Row( children: [ Expanded( @@ -40,10 +34,7 @@ class ModeSelectorWidget extends StatelessWidget { value: value, iconSize: 30.0, isExpanded: true, - style: TextStyle( - fontSize: valueFontSize ?? Sizes.largeFontSize, - color: Colors.black, - ), + style: Theme.of(context).textTheme.title, hint: Text("Select ${caption.toLowerCase()}"), items: options.map((value) { return new DropdownMenuItem( diff --git a/lib/entities/climate/widgets/mode_swicth.dart b/lib/entities/climate/widgets/mode_swicth.dart index 9051fd9..68ef40b 100644 --- a/lib/entities/climate/widgets/mode_swicth.dart +++ b/lib/entities/climate/widgets/mode_swicth.dart @@ -4,7 +4,6 @@ class ModeSwitchWidget extends StatelessWidget { final String caption; final onChange; - final double captionFontSize; final bool value; final bool expanded; final EdgeInsets padding; @@ -13,7 +12,6 @@ class ModeSwitchWidget extends StatelessWidget { Key key, @required this.caption, @required this.onChange, - this.captionFontSize, this.value, this.expanded: true, this.padding: const EdgeInsets.only(left: Sizes.leftWidgetPadding, right: Sizes.rightWidgetPadding) @@ -25,7 +23,7 @@ class ModeSwitchWidget extends StatelessWidget { padding: this.padding, child: Row( children: [ - _buildCaption(), + _buildCaption(context), Switch( onChanged: (value) => onChange(value), value: value ?? false, @@ -35,12 +33,10 @@ class ModeSwitchWidget extends StatelessWidget { ); } - Widget _buildCaption() { + Widget _buildCaption(BuildContext context) { Widget captionWidget = Text( "$caption", - style: TextStyle( - fontSize: captionFontSize ?? Sizes.stateFontSize - ), + style: Theme.of(context).textTheme.body1, ); if (expanded) { return Expanded( diff --git a/lib/entities/climate/widgets/temperature_control_widget.dart b/lib/entities/climate/widgets/temperature_control_widget.dart index 004a304..644820b 100644 --- a/lib/entities/climate/widgets/temperature_control_widget.dart +++ b/lib/entities/climate/widgets/temperature_control_widget.dart @@ -2,8 +2,7 @@ part of '../../../main.dart'; class TemperatureControlWidget extends StatelessWidget { final double value; - final double fontSize; - final Color fontColor; + final bool active; final onInc; final onDec; @@ -12,8 +11,9 @@ class TemperatureControlWidget extends StatelessWidget { @required this.value, @required this.onInc, @required this.onDec, - this.fontSize, - this.fontColor}) + //this.fontSize, + this.active: false + }) : super(key: key); @override @@ -23,10 +23,7 @@ class TemperatureControlWidget extends StatelessWidget { children: [ Text( "$value", - style: TextStyle( - fontSize: fontSize ?? 24.0, - color: fontColor ?? Colors.black - ), + style: active ? Theme.of(context).textTheme.display2 : Theme.of(context).textTheme.display1, ), Column( children: [ diff --git a/lib/entities/entity_name.widget.dart b/lib/entities/entity_name.widget.dart index ed7c10c..5ecd3aa 100644 --- a/lib/entities/entity_name.widget.dart +++ b/lib/entities/entity_name.widget.dart @@ -5,18 +5,21 @@ class EntityName extends StatelessWidget { final EdgeInsetsGeometry padding; final TextOverflow textOverflow; final bool wordsWrap; - final double fontSize; final TextAlign textAlign; final int maxLines; + final TextStyle textStyle; - const EntityName({Key key, this.maxLines, this.padding: const EdgeInsets.only(right: 10.0), this.textOverflow: TextOverflow.ellipsis, this.wordsWrap: true, this.fontSize: Sizes.nameFontSize, this.textAlign: TextAlign.left}) : super(key: key); + const EntityName({Key key, this.maxLines, this.padding: const EdgeInsets.only(right: 10.0), this.textOverflow: TextOverflow.ellipsis, this.textStyle, this.wordsWrap: true, this.textAlign: TextAlign.left}) : super(key: key); @override Widget build(BuildContext context) { final EntityWrapper entityWrapper = EntityModel.of(context).entityWrapper; - TextStyle textStyle = TextStyle(fontSize: fontSize); - if (entityWrapper.entity.statelessType == StatelessEntityType.WEBLINK) { - textStyle = textStyle.apply(color: Colors.blue, decoration: TextDecoration.underline); + TextStyle tStyle; + if (textStyle == null) { + tStyle = Theme.of(context).textTheme.body1; + if (entityWrapper.entity.statelessType == StatelessEntityType.WEBLINK) { + tStyle = tStyle.apply(color: Colors.blue, decoration: TextDecoration.underline); + } } return Padding( padding: padding, @@ -25,7 +28,7 @@ class EntityName extends StatelessWidget { overflow: textOverflow, softWrap: wordsWrap, maxLines: maxLines, - style: textStyle, + style: tStyle, textAlign: textAlign, ), ); diff --git a/lib/main.dart b/lib/main.dart index 68dfd8b..aebf7e8 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,7 +1,7 @@ import 'dart:convert'; import 'dart:async'; import 'dart:math'; -import 'dart:io'; +//import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/material.dart'; @@ -10,7 +10,7 @@ import 'package:web_socket_channel/io.dart'; import 'package:event_bus/event_bus.dart'; import 'package:flutter/widgets.dart'; import 'package:cached_network_image/cached_network_image.dart'; -import 'package:path_provider/path_provider.dart'; +//import 'package:path_provider/path_provider.dart'; import 'package:url_launcher/url_launcher.dart' as urlLauncher; import 'package:flutter/services.dart'; import 'package:date_format/date_format.dart'; @@ -35,6 +35,7 @@ import 'package:flutter_webview_plugin/flutter_webview_plugin.dart' as standalon import 'package:webview_flutter/webview_flutter.dart'; import 'package:video_player/video_player.dart'; +import 'managers/theme_manager.dart'; import 'utils/logger.dart'; part 'const.dart'; @@ -226,9 +227,8 @@ class _HAClientAppState extends State { Widget build(BuildContext context) { return new MaterialApp( title: appName, - theme: new ThemeData( - primarySwatch: Colors.blue, - ), + theme: HAClientTheme().lightTheme, + darkTheme: HAClientTheme().darkTheme, initialRoute: "/", routes: { "/": (context) => MainPage(title: 'HA Client'), diff --git a/lib/managers/theme_manager.dart b/lib/managers/theme_manager.dart new file mode 100644 index 0000000..6e2a268 --- /dev/null +++ b/lib/managers/theme_manager.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; + +class HAClientTheme { + + static const Color defaultFontColor = Colors.black87; + + static final HAClientTheme _instance = HAClientTheme + ._internal(); + + factory HAClientTheme() { + return _instance; + } + + HAClientTheme._internal(); + + final ThemeData lightTheme = ThemeData.light().copyWith( + textTheme: ThemeData.light().textTheme.copyWith( + display1: TextStyle(fontSize: 34, fontWeight: FontWeight.normal, color: Colors.black54), + display2: TextStyle(fontSize: 34, fontWeight: FontWeight.normal, color: Colors.redAccent), + headline: TextStyle(fontSize: 24, fontWeight: FontWeight.normal, color: defaultFontColor), + title: TextStyle(fontSize: 20, fontWeight: FontWeight.w700, color: defaultFontColor), + subhead: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: defaultFontColor), + body1: TextStyle(fontSize: 15, fontWeight: FontWeight.normal, color: defaultFontColor), + body2: TextStyle(fontSize: 15, fontWeight: FontWeight.w500, color: defaultFontColor), + subtitle: TextStyle(fontSize: 15, fontWeight: FontWeight.w500, color: Colors.black45), + caption: TextStyle(fontSize: 13, fontWeight: FontWeight.normal, color: defaultFontColor), + overline: TextStyle(fontSize: 10, fontWeight: FontWeight.normal, color: Colors.black54), + ) + ); + + final ThemeData darkTheme = ThemeData.dark(); + +} \ No newline at end of file diff --git a/lib/pages/integration_settings.page.dart b/lib/pages/integration_settings.page.dart index b6326b3..7831795 100644 --- a/lib/pages/integration_settings.page.dart +++ b/lib/pages/integration_settings.page.dart @@ -115,7 +115,7 @@ class _IntegrationSettingsPageState extends State { scrollDirection: Axis.vertical, padding: const EdgeInsets.all(20.0), children: [ - Text("Location tracking", style: TextStyle(fontSize: Sizes.largeFontSize-2)), + Text("Location tracking", style: Theme.of(context).textTheme.title), Container(height: Sizes.rowPadding,), InkWell( onTap: () => Launcher.launchURLInCustomTab(context: context, url: "http://ha-client.app/docs#location-tracking"), @@ -153,21 +153,24 @@ class _IntegrationSettingsPageState extends State { //Expanded(child: Container(),), FlatButton( padding: EdgeInsets.all(0.0), - child: Text("-", style: TextStyle(fontSize: Sizes.largeFontSize)), + child: Text("-", style: Theme.of(context).textTheme.title), onPressed: () => decLocationInterval(), ), - Text("$_locationInterval", style: TextStyle(fontSize: Sizes.largeFontSize)), + Text("$_locationInterval", style: Theme.of(context).textTheme.title), FlatButton( padding: EdgeInsets.all(0.0), - child: Text("+", style: TextStyle(fontSize: Sizes.largeFontSize)), + child: Text("+", style: Theme.of(context).textTheme.title), onPressed: () => incLocationInterval(), ), ], ), Divider(), - Text("Integration status", style: TextStyle(fontSize: Sizes.largeFontSize-2)), + Text("Integration status", style: Theme.of(context).textTheme.title), Container(height: Sizes.rowPadding,), - Text("${HomeAssistant().userName}'s ${DeviceInfoManager().model}, ${DeviceInfoManager().osName} ${DeviceInfoManager().osVersion}"), + Text( + "${HomeAssistant().userName}'s ${DeviceInfoManager().model}, ${DeviceInfoManager().osName} ${DeviceInfoManager().osVersion}", + style: Theme.of(context).textTheme.subtitle, + ), Container(height: 6.0,), Text("Here you can manually check if HA Client integration with your Home Assistant works fine. As mobileApp integration in Home Assistant is still in development, this is not 100% correct check."), //Divider(),