diff --git a/lib/Frontend/Widgets/known_gear.dart b/lib/Frontend/Widgets/known_gear.dart index cf2f8fae..9cf36035 100644 --- a/lib/Frontend/Widgets/known_gear.dart +++ b/lib/Frontend/Widgets/known_gear.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:tail_app/Backend/Definitions/Device/device_definition.dart'; import 'package:tail_app/Frontend/Widgets/scan_for_new_device.dart'; +import 'package:tail_app/Frontend/utils.dart'; import '../../Backend/Bluetooth/bluetooth_manager.dart'; import '../../Backend/LoggingWrappers.dart'; @@ -44,60 +45,61 @@ class ScanForNewGearButton extends ConsumerWidget { tween: ref.watch(knownDevicesProvider).isEmpty ? Tween(begin: 0, end: 1) : Tween(begin: 1, end: 0), duration: animationTransitionDuration, builder: (context, value, child) { + Color? color = Color.lerp(Theme.of(context).cardColor, Theme.of(context).primaryColor, value); return Card( clipBehavior: Clip.antiAlias, - color: Color.lerp(Theme.of(context).cardColor, Theme.of(context).primaryColor, value), - child: child, - ); - }, - child: InkWell( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: SizedBox( - height: 50 * MediaQuery.textScalerOf(context).scale(1), - width: ref.watch(knownDevicesProvider).values.length > 1 ? 100 * MediaQuery.textScalerOf(context).scale(1) : 200 * MediaQuery.textScalerOf(context).scale(1), - child: Center( - child: Text( - scanDevicesTitle(), - textAlign: TextAlign.center, + color: color, + child: InkWell( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: SizedBox( + height: 50 * MediaQuery.textScalerOf(context).scale(1), + width: ref.watch(knownDevicesProvider).values.length > 1 ? 100 * MediaQuery.textScalerOf(context).scale(1) : 200 * MediaQuery.textScalerOf(context).scale(1), + child: Center( + child: Text( + scanDevicesTitle(), + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.labelLarge!.copyWith(color: getTextColor(color!)), + ), + ), ), ), - ), - ), - onTap: () { - plausible.event(page: "Scan For New Gear"); - showModalBottomSheet( - context: context, - showDragHandle: true, - isScrollControlled: true, - enableDrag: true, - isDismissible: true, - builder: (BuildContext context) { - return DraggableScrollableSheet( - initialChildSize: 0.5, - expand: false, - builder: (BuildContext context, ScrollController scrollController) { - return Column( - children: [ - ListTile( - title: Text( - scanDevicesTitle(), - style: Theme.of(context).textTheme.titleLarge, - ), - ), - Expanded( - child: ScanForNewDevice( - scrollController: scrollController, - ), - ), - ], + onTap: () { + plausible.event(page: "Scan For New Gear"); + showModalBottomSheet( + context: context, + showDragHandle: true, + isScrollControlled: true, + enableDrag: true, + isDismissible: true, + builder: (BuildContext context) { + return DraggableScrollableSheet( + initialChildSize: 0.5, + expand: false, + builder: (BuildContext context, ScrollController scrollController) { + return Column( + children: [ + ListTile( + title: Text( + scanDevicesTitle(), + style: Theme.of(context).textTheme.titleLarge, + ), + ), + Expanded( + child: ScanForNewDevice( + scrollController: scrollController, + ), + ), + ], + ); + }, ); }, ); }, - ); - }, - ), + ), + ); + }, ); } } @@ -117,14 +119,14 @@ class _KnownGearCardState extends ConsumerState { return FadeIn( child: ValueListenableBuilder( valueListenable: widget.baseStatefulDevice.deviceConnectionState, - builder: (BuildContext context, ConnectivityState value, Widget? child) { + builder: (BuildContext context, ConnectivityState connectivityState, Widget? child) { return Flash( - animate: value == ConnectivityState.connected, + animate: connectivityState == ConnectivityState.connected, child: ValueListenableBuilder( valueListenable: widget.baseStatefulDevice.hasUpdate, - builder: (BuildContext context, bool value, Widget? child) { + builder: (BuildContext context, bool hasUpdate, Widget? child) { return Badge( - isLabelVisible: value, + isLabelVisible: hasUpdate, largeSize: 35, backgroundColor: Theme.of(context).primaryColor, label: const Icon(Icons.system_update), @@ -132,113 +134,115 @@ class _KnownGearCardState extends ConsumerState { ); }, child: TweenAnimationBuilder( - tween: value == ConnectivityState.connected ? Tween(begin: 0, end: 1) : Tween(begin: 1, end: 0), + tween: connectivityState == ConnectivityState.connected ? Tween(begin: 0, end: 1) : Tween(begin: 1, end: 0), duration: animationTransitionDuration, - child: InkWell( - onTap: () { - plausible.event(page: "Manage Gear"); - showModalBottomSheet( - context: context, - showDragHandle: true, - isScrollControlled: true, - enableDrag: true, - isDismissible: true, - builder: (BuildContext context) { - return DraggableScrollableSheet( - expand: false, - initialChildSize: 0.7, - builder: (BuildContext context, ScrollController scrollController) { - return ManageGear( - ref: ref, - device: widget.baseStatefulDevice, - controller: scrollController, + builder: (BuildContext context, double value, Widget? child) { + Color? cardColor = Color.lerp(Theme.of(context).cardColor, Color(widget.baseStatefulDevice.baseStoredDevice.color), value); + Color textColor = getTextColor(cardColor!); + return Card( + clipBehavior: Clip.antiAlias, + color: cardColor, + child: InkWell( + onTap: () { + plausible.event(page: "Manage Gear"); + showModalBottomSheet( + context: context, + showDragHandle: true, + isScrollControlled: true, + enableDrag: true, + isDismissible: true, + builder: (BuildContext context) { + return DraggableScrollableSheet( + expand: false, + initialChildSize: 0.7, + builder: (BuildContext context, ScrollController scrollController) { + return ManageGear( + ref: ref, + device: widget.baseStatefulDevice, + controller: scrollController, + ); + }, ); }, - ); + ).then((value) { + setState(() {}); //force widget update + return; + }); }, - ).then((value) { - setState(() {}); //force widget update - return; - }); - }, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: SizedBox( - height: 50, - width: 100, - child: Stack( - children: [ - Text( - widget.baseStatefulDevice.baseStoredDevice.name, - overflow: TextOverflow.ellipsis, - ), - Padding( - padding: const EdgeInsets.only(top: 16), - child: Align( - alignment: Alignment.bottomCenter, - child: AnimatedCrossFade( - firstChild: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ValueListenableBuilder( - valueListenable: widget.baseStatefulDevice.batteryLevel, - builder: (BuildContext context, value, Widget? child) { - return AnimatedSwitcher( - duration: animationTransitionDuration, - child: getBattery(value), - ); - }, - ), - ValueListenableBuilder( - valueListenable: widget.baseStatefulDevice.batteryCharging, - builder: (BuildContext context, value, Widget? child) { - return AnimatedCrossFade( - firstChild: const Icon(Icons.power), - secondChild: Container(), - crossFadeState: widget.baseStatefulDevice.deviceConnectionState.value == ConnectivityState.connected && value ? CrossFadeState.showFirst : CrossFadeState.showSecond, - duration: animationTransitionDuration, - ); - }, - ), - ValueListenableBuilder( - valueListenable: widget.baseStatefulDevice.mandatoryOtaRequired, - builder: (BuildContext context, value, Widget? child) { - return AnimatedCrossFade( - firstChild: Flash(child: const Icon(Icons.warning)), - secondChild: Container(), - crossFadeState: value ? CrossFadeState.showFirst : CrossFadeState.showSecond, - duration: animationTransitionDuration, - ); - }, - ), - widget.baseStatefulDevice.baseDeviceDefinition.unsupported ? const Icon(Icons.warning) : Container(), - ValueListenableBuilder( - valueListenable: widget.baseStatefulDevice.rssi, - builder: (BuildContext context, value, Widget? child) { - return AnimatedSwitcher( - duration: animationTransitionDuration, - child: getSignal(value), - ); - }, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: SizedBox( + height: 50, + width: 100, + child: Stack( + children: [ + Text( + widget.baseStatefulDevice.baseStoredDevice.name, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.labelLarge!.copyWith(color: textColor), + ), + Padding( + padding: const EdgeInsets.only(top: 16), + child: Align( + alignment: Alignment.bottomCenter, + child: AnimatedCrossFade( + firstChild: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ValueListenableBuilder( + valueListenable: widget.baseStatefulDevice.batteryLevel, + builder: (BuildContext context, batteryLevel, Widget? child) { + return AnimatedSwitcher( + duration: animationTransitionDuration, + child: getBattery(batteryLevel, textColor), + ); + }, + ), + ValueListenableBuilder( + valueListenable: widget.baseStatefulDevice.batteryCharging, + builder: (BuildContext context, batteryCharging, Widget? child) { + return AnimatedCrossFade( + firstChild: Icon(Icons.power, color: textColor), + secondChild: Container(), + crossFadeState: batteryCharging ? CrossFadeState.showFirst : CrossFadeState.showSecond, + duration: animationTransitionDuration, + ); + }, + ), + ValueListenableBuilder( + valueListenable: widget.baseStatefulDevice.mandatoryOtaRequired, + builder: (BuildContext context, otaRequired, Widget? child) { + return AnimatedCrossFade( + firstChild: Flash(child: Icon(Icons.warning, color: textColor)), + secondChild: Container(), + crossFadeState: otaRequired ? CrossFadeState.showFirst : CrossFadeState.showSecond, + duration: animationTransitionDuration, + ); + }, + ), + widget.baseStatefulDevice.baseDeviceDefinition.unsupported ? Icon(Icons.warning, color: textColor) : Container(), + ValueListenableBuilder( + valueListenable: widget.baseStatefulDevice.rssi, + builder: (BuildContext context, rssi, Widget? child) { + return AnimatedSwitcher( + duration: animationTransitionDuration, + child: getSignal(rssi, textColor), + ); + }, + ), + ], ), - ], + secondChild: Icon(Icons.bluetooth_disabled, color: textColor), + crossFadeState: connectivityState == ConnectivityState.connected ? CrossFadeState.showFirst : CrossFadeState.showSecond, + duration: animationTransitionDuration, + ), ), - secondChild: const Icon(Icons.bluetooth_disabled), - crossFadeState: value == ConnectivityState.connected ? CrossFadeState.showFirst : CrossFadeState.showSecond, - duration: animationTransitionDuration, - ), - ), - ) - ], + ) + ], + ), + ), ), ), - ), - ), - builder: (BuildContext context, double value, Widget? child) { - return Card( - clipBehavior: Clip.antiAlias, - color: Color.lerp(Theme.of(context).cardColor, Color(widget.baseStatefulDevice.baseStoredDevice.color), value), - child: child, ); }, ), @@ -249,49 +253,58 @@ class _KnownGearCardState extends ConsumerState { ); } - Widget getSignal(int rssi) { + Widget getSignal(int rssi, Color color) { if (rssi == -1) { // Not Connected - return const Icon(Icons.signal_cellular_connected_no_internet_0_bar); + return Icon(Icons.signal_cellular_connected_no_internet_0_bar, color: color); } else if (rssi < -80) { - return const Icon(Icons.signal_cellular_alt_1_bar); + return Icon(Icons.signal_cellular_alt_1_bar, color: color); } else if (rssi < -60) { - return const Icon(Icons.signal_cellular_alt_2_bar); + return Icon(Icons.signal_cellular_alt_2_bar, color: color); } else { - return const Icon(Icons.signal_cellular_alt); + return Icon(Icons.signal_cellular_alt, color: color); } } - Widget getBattery(double level) { + Widget getBattery(double level, Color color) { if (HiveProxy.getOrDefault(settings, showAccurateBattery, defaultValue: showAccurateBatteryDefault)) { if (level < 0) { // battery level is unknown - return const Text('?%'); + return Text( + '?%', + style: Theme.of(context).textTheme.labelLarge!.copyWith(color: color), + ); } - return Text('${level.toInt()}%'); + return Text( + '${level.toInt()}%', + style: Theme.of(context).textTheme.labelLarge!.copyWith(color: color), + ); } if (level < 0) { - return const Icon(Icons.battery_unknown); + return Icon(Icons.battery_unknown, color: color); } if (level < 12.5) { - return Flash(infinite: true, child: const Icon(Icons.battery_0_bar)); + return Flash(infinite: true, child: Icon(Icons.battery_0_bar, color: color)); } else if (level < 25) { return Flash( infinite: true, - child: const Icon(Icons.battery_1_bar), + child: Icon( + Icons.battery_1_bar, + color: color, + ), ); } else if (level < 37.5) { - return const Icon(Icons.battery_2_bar); + return Icon(Icons.battery_2_bar, color: color); } else if (level < 50) { - return const Icon(Icons.battery_3_bar); + return Icon(Icons.battery_3_bar, color: color); } else if (level < 62.5) { - return const Icon(Icons.battery_4_bar); + return Icon(Icons.battery_4_bar, color: color); } else if (level < 75) { - return const Icon(Icons.battery_5_bar); + return Icon(Icons.battery_5_bar, color: color); } else if (level < 87.5) { - return const Icon(Icons.battery_6_bar); + return Icon(Icons.battery_6_bar, color: color); } else { - return const Icon(Icons.battery_full); + return Icon(Icons.battery_full, color: color); } } } diff --git a/lib/Frontend/pages/actions.dart b/lib/Frontend/pages/actions.dart index d214b3ed..fb38ae7e 100644 --- a/lib/Frontend/pages/actions.dart +++ b/lib/Frontend/pages/actions.dart @@ -18,6 +18,7 @@ import '../../Backend/move_lists.dart'; import '../../constants.dart'; import '../Widgets/tutorial_card.dart'; import '../translation_string_definitions.dart'; +import '../utils.dart'; import 'home.dart'; class ActionPage extends ConsumerWidget { @@ -135,11 +136,13 @@ class _ActionPageBuilderState extends ConsumerState { } FadeIn getActionCard(int actionIndex, Map knownDevices, BaseAction action, bool largerCards) { + Color color = Color(knownDevices.values.where((element) => element.deviceConnectionState.value == ConnectivityState.connected).where((element) => action.deviceCategory.contains(element.baseDeviceDefinition.deviceType)).first.baseStoredDevice.color); + Color textColor = getTextColor(color); return FadeIn( delay: Duration(milliseconds: 100 * actionIndex), child: Card( clipBehavior: Clip.antiAlias, - color: Color(knownDevices.values.where((element) => element.deviceConnectionState.value == ConnectivityState.connected).where((element) => action.deviceCategory.contains(element.baseDeviceDefinition.deviceType)).first.baseStoredDevice.color), + color: color, elevation: 1, child: InkWell( onLongPress: () { @@ -195,6 +198,7 @@ class _ActionPageBuilderState extends ConsumerState { (e) => Text( e.baseDeviceDefinition.deviceType.name.substring(0, 1), textScaler: TextScaler.linear(largerCards ? 2 : 1), + style: Theme.of(context).textTheme.labelLarge!.copyWith(color: textColor), ), ) .toList(), @@ -207,7 +211,7 @@ class _ActionPageBuilderState extends ConsumerState { child: ref.read(favoriteActionsProvider.notifier).contains(action) ? Transform.scale( scale: largerCards ? 1.8 : 0.8, - child: const Icon(Icons.favorite), + child: Icon(Icons.favorite, color: textColor), ) : null, ), @@ -218,6 +222,7 @@ class _ActionPageBuilderState extends ConsumerState { semanticsLabel: action.name, overflow: TextOverflow.fade, textAlign: TextAlign.center, + style: Theme.of(context).textTheme.labelLarge!.copyWith(color: textColor), textScaler: TextScaler.linear(largerCards ? 2 : 1), ), ) diff --git a/lib/Frontend/utils.dart b/lib/Frontend/utils.dart index 01b30877..73c724a4 100644 --- a/lib/Frontend/utils.dart +++ b/lib/Frontend/utils.dart @@ -1,6 +1,7 @@ import 'package:cross_platform/cross_platform.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; import 'package:logarte/logarte.dart'; import 'package:logging/logging.dart'; import 'package:native_dio_adapter/native_dio_adapter.dart'; @@ -80,3 +81,16 @@ Version getVersionSemVer(String input) { } return Version(int.parse(major), int.parse(minor), int.parse(patch)); } + +Color getTextColor(Color color) { + int d = 0; + + // Counting the perceptive luminance - human eye favors green color... + double luminance = (0.299 * color.red + 0.587 * color.green + 0.114 * color.blue) / 255; + + if (luminance > 0.5) { + return Typography.material2021().black.labelLarge!.color!; + } else { + return Typography.material2021().white.labelLarge!.color!; + } +} diff --git a/lib/main.dart b/lib/main.dart index 4ddad6ee..35871974 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -22,6 +22,7 @@ import 'package:tail_app/Backend/Definitions/Device/device_definition.dart'; import 'package:tail_app/Backend/action_registry.dart'; import 'package:tail_app/Backend/wear_bridge.dart'; import 'package:tail_app/Frontend/Widgets/bt_app_state_controller.dart'; +import 'package:tail_app/Frontend/utils.dart'; import 'Backend/Definitions/Action/base_action.dart'; import 'Backend/LoggingWrappers.dart'; @@ -250,7 +251,12 @@ ThemeData buildTheme(Brightness brightness, Color color) { ), appBarTheme: const AppBarTheme(elevation: 2), // We use the nicer Material-3 Typography in both M2 and M3 mode. - typography: Typography.material2021(platform: defaultTargetPlatform), + typography: Typography.material2021(), + filledButtonTheme: FilledButtonThemeData( + style: ElevatedButton.styleFrom( + foregroundColor: getTextColor(color), + ), + ), ); } else { return ThemeData( @@ -261,7 +267,12 @@ ThemeData buildTheme(Brightness brightness, Color color) { ), appBarTheme: const AppBarTheme(elevation: 2), // We use the nicer Material-3 Typography in both M2 and M3 mode. - typography: Typography.material2021(platform: defaultTargetPlatform), + typography: Typography.material2021(), + filledButtonTheme: FilledButtonThemeData( + style: ElevatedButton.styleFrom( + foregroundColor: getTextColor(color), + ), + ), ); } }