Skip to content

Commit

Permalink
feat(battery_level_icon.dart,battery_level_provider.dart,battery_widg…
Browse files Browse the repository at this point in the history
…et.dart,widget_style_constants.dart,battery_widget_test.dart,battery_widget_test.mocks.dart): Add battery icon display content
  • Loading branch information
rlperez committed Dec 20, 2021
1 parent 2f864e5 commit 55bdf09
Show file tree
Hide file tree
Showing 6 changed files with 280 additions and 0 deletions.
22 changes: 22 additions & 0 deletions lib/assets/traits/battery_level_icon.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import 'package:bootstrap_icons/bootstrap_icons.dart';
import 'package:flutter/material.dart';
import 'package:yonomi_device_widgets/ui/widget_style_constants.dart';

class BatteryLevelIcon extends Icon {
BatteryLevelIcon(int batteryLevel,
{size = WidgetStyleConstants.defaultDeviceIconSize,
color = WidgetStyleConstants.deviceIconColor,
Key? key})
: super(_getBatteryLevelIcon(batteryLevel),
key: key, size: size, color: color);

static IconData _getBatteryLevelIcon(int batteryLevel) {
if (batteryLevel >= WidgetStyleConstants.batteryFullMin) {
return BootstrapIcons.battery_full;
} else if (batteryLevel <= WidgetStyleConstants.batteryLowMax) {
return BootstrapIcons.battery;
} else {
return BootstrapIcons.battery_half;
}
}
}
54 changes: 54 additions & 0 deletions lib/providers/battery_level_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import 'package:flutter/material.dart';
import 'package:yonomi_device_widgets/providers/power_trait_provider.dart';
import 'package:yonomi_device_widgets/providers/widget_state.dart';
import 'package:yonomi_platform_sdk/yonomi-sdk.dart';

class BatteryLevelTraitProvider extends ChangeNotifier {
Device? _deviceDetail;

late String _deviceId;

late Request _request;

WidgetState _state = WidgetState.idle;

BatteryLevelTraitProvider(Request request, String deviceId,
{GetDeviceDetailsMethod getDetails =
DevicesRepository.getDeviceDetails}) {
this._deviceId = deviceId;
this._request = request;
fetchData(getDeviceDetails: getDetails);
}

Device? get deviceDetail => _deviceDetail;

Future<Device?> fetchData(
{GetDeviceDetailsMethod getDeviceDetails =
DevicesRepository.getDeviceDetails}) async {
_state = WidgetState.loading;

try {
_deviceDetail = await getDeviceDetails(_request, _deviceId);
} catch (error) {
_state = WidgetState.error;
return null;
}

_state = WidgetState.idle;
return deviceDetail;
}

BatteryLevelTrait? getBatteryLevelTrait() {
return deviceDetail?.traits
.firstWhere((trait) => trait.runtimeType is BatteryLevelTrait)
as BatteryLevelTrait;
}

int get getBatteryLevel {
return getBatteryLevelTrait()?.state.value ?? 0;
}

bool get isLoading => _state == WidgetState.loading;

bool get isInErrorState => _state == WidgetState.error;
}
43 changes: 43 additions & 0 deletions lib/traits/battery_widget.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
import 'package:yonomi_device_widgets/assets/traits/battery_level_icon.dart';
import 'package:yonomi_device_widgets/providers/battery_level_provider.dart';

class BatteryWidget extends StatelessWidget {
final BatteryLevelTraitProvider _batteryLevelTraitProvider;

BatteryWidget(this._batteryLevelTraitProvider);

@override
Widget build(BuildContext context) {
if (_batteryLevelTraitProvider.isLoading) {
return CircularProgressIndicator();
} else if (_batteryLevelTraitProvider.isInErrorState) {
return Icon(Icons.error);
} else {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Row(children: <Widget>[
Text(
'BATTERY',
style: Theme.of(context).textTheme.headline6,
)
]),
SizedBox(
height: 10,
),
Container(
child: SizedBox(
width: 100,
height: 100,
child: BatteryLevelIcon(
_batteryLevelTraitProvider.getBatteryLevel,
size: 100.0,
color: Colors.white,
),
),
)
]);
}
}
}
4 changes: 4 additions & 0 deletions lib/ui/widget_style_constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,8 @@ class WidgetStyleConstants {
static const double defaultDeviceWidgetSize = 175.0;

static const Color traitDetailSwitchPressedColor = Color(0xFF01A299);

static const int batteryLowMax = 15;

static const int batteryFullMin = 75;
}
82 changes: 82 additions & 0 deletions test/traits/battery_widget_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import 'dart:math';

import 'package:bootstrap_icons/bootstrap_icons.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:yonomi_device_widgets/assets/traits/battery_level_icon.dart';
import 'package:yonomi_device_widgets/providers/battery_level_provider.dart';
import 'package:yonomi_device_widgets/traits/battery_widget.dart';
import 'package:yonomi_device_widgets/ui/widget_style_constants.dart';

import 'battery_widget_test.mocks.dart';

MaterialApp createMaterialApp(
BatteryLevelTraitProvider mockBatteryLevelProvider) {
return MaterialApp(
home: Column(children: [BatteryWidget(mockBatteryLevelProvider)]),
);
}

@GenerateMocks([BatteryLevelTraitProvider])
void main() {
testWidgets('When loading, should show CircularProgressIndicator ',
(WidgetTester tester) async {
final mockBatteryLevelProvider = MockBatteryLevelTraitProvider();
when(mockBatteryLevelProvider.isLoading).thenReturn(true);
when(mockBatteryLevelProvider.isInErrorState).thenReturn(false);

await tester.pumpWidget(createMaterialApp(mockBatteryLevelProvider));

expect(find.byType(CircularProgressIndicator), findsOneWidget);
});

testWidgets('When battery level is low, should show low battery icon',
(WidgetTester tester) async {
final mockBatteryLevelProvider = MockBatteryLevelTraitProvider();
when(mockBatteryLevelProvider.isLoading).thenReturn(false);
when(mockBatteryLevelProvider.isInErrorState).thenReturn(false);
when(mockBatteryLevelProvider.getBatteryLevel)
.thenReturn(WidgetStyleConstants.batteryLowMax);

await tester.pumpWidget(createMaterialApp(mockBatteryLevelProvider));

expect(find.byType(CircularProgressIndicator), findsNothing);
expect(find.byType(BatteryWidget), findsOneWidget);
expect(find.byType(BatteryLevelIcon), findsOneWidget);
expect(find.byIcon(BootstrapIcons.battery), findsOneWidget);
});

testWidgets(
'When battery level is not low or high, should show half battery icon',
(WidgetTester tester) async {
final mockBatteryLevelProvider = MockBatteryLevelTraitProvider();
when(mockBatteryLevelProvider.isLoading).thenReturn(false);
when(mockBatteryLevelProvider.isInErrorState).thenReturn(false);
when(mockBatteryLevelProvider.getBatteryLevel).thenReturn(50);

await tester.pumpWidget(createMaterialApp(mockBatteryLevelProvider));

expect(find.byType(CircularProgressIndicator), findsNothing);
expect(find.byType(BatteryWidget), findsOneWidget);
expect(find.byType(BatteryLevelIcon), findsOneWidget);
expect(find.byIcon(BootstrapIcons.battery_half), findsOneWidget);
});

testWidgets('When battery level is high, should show full battery icon',
(WidgetTester tester) async {
final mockBatteryLevelProvider = MockBatteryLevelTraitProvider();
when(mockBatteryLevelProvider.isLoading).thenReturn(false);
when(mockBatteryLevelProvider.isInErrorState).thenReturn(false);
when(mockBatteryLevelProvider.getBatteryLevel)
.thenReturn(WidgetStyleConstants.batteryFullMin);

await tester.pumpWidget(createMaterialApp(mockBatteryLevelProvider));

expect(find.byType(CircularProgressIndicator), findsNothing);
expect(find.byType(BatteryWidget), findsOneWidget);
expect(find.byType(BatteryLevelIcon), findsOneWidget);
expect(find.byIcon(BootstrapIcons.battery_full), findsOneWidget);
});
}
75 changes: 75 additions & 0 deletions test/traits/battery_widget_test.mocks.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Mocks generated by Mockito 5.0.15 from annotations
// in yonomi_device_widgets/test/traits/battery_widget_test.dart.
// Do not manually edit this file.

import 'dart:async' as _i3;
import 'dart:ui' as _i7;

import 'package:mockito/mockito.dart' as _i1;
import 'package:yonomi_device_widgets/providers/battery_level_provider.dart'
as _i2;
import 'package:yonomi_device_widgets/providers/power_trait_provider.dart'
as _i5;
import 'package:yonomi_platform_sdk/src/repository/devices/devices_repository.dart'
as _i6;
import 'package:yonomi_platform_sdk/yonomi-sdk.dart' as _i4;

// ignore_for_file: avoid_redundant_argument_values
// ignore_for_file: avoid_setters_without_getters
// ignore_for_file: comment_references
// ignore_for_file: implementation_imports
// ignore_for_file: invalid_use_of_visible_for_testing_member
// ignore_for_file: prefer_const_constructors
// ignore_for_file: unnecessary_parenthesis

/// A class which mocks [BatteryLevelTraitProvider].
///
/// See the documentation for Mockito's code generation for more information.
class MockBatteryLevelTraitProvider extends _i1.Mock
implements _i2.BatteryLevelTraitProvider {
MockBatteryLevelTraitProvider() {
_i1.throwOnMissingStub(this);
}

@override
int get getBatteryLevel =>
(super.noSuchMethod(Invocation.getter(#getBatteryLevel), returnValue: 0)
as int);
@override
bool get isLoading =>
(super.noSuchMethod(Invocation.getter(#isLoading), returnValue: false)
as bool);
@override
bool get isInErrorState => (super
.noSuchMethod(Invocation.getter(#isInErrorState), returnValue: false)
as bool);
@override
bool get hasListeners =>
(super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false)
as bool);
@override
_i3.Future<_i4.Device?> fetchData(
{_i5.GetDeviceDetailsMethod? getDeviceDetails =
_i6.DevicesRepository.getDeviceDetails}) =>
(super.noSuchMethod(
Invocation.method(
#fetchData, [], {#getDeviceDetails: getDeviceDetails}),
returnValue: Future<_i4.Device?>.value()) as _i3.Future<_i4.Device?>);
@override
void addListener(_i7.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);
@override
void removeListener(_i7.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#removeListener, [listener]),
returnValueForMissingStub: null);
@override
void dispose() => super.noSuchMethod(Invocation.method(#dispose, []),
returnValueForMissingStub: null);
@override
void notifyListeners() =>
super.noSuchMethod(Invocation.method(#notifyListeners, []),
returnValueForMissingStub: null);
@override
String toString() => super.toString();
}

0 comments on commit 55bdf09

Please sign in to comment.