Instantly share code, notes, and snippets.
Last active
November 14, 2022 20:40
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save StanislawNagorski/403eabccdc5529af3159caff881fdb4b to your computer and use it in GitHub Desktop.
Button with 3 states: ON, OFF, DISABLE
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'package:flutter_hooks/flutter_hooks.dart'; | |
import '../presentation/values/values.dart'; | |
class TextToggleOnOffButton extends HookWidget { | |
const TextToggleOnOffButton({ | |
required this.buttonContent, | |
required this.onToggleOn, | |
required this.backgroundColor, | |
this.onToggleOff, | |
this.backgroundColorOnToggleOn, | |
this.buttonContentOnToggleOn, | |
this.enableToggleOnOff = false, | |
super.key, | |
}) : assert( | |
(enableToggleOnOff && backgroundColorOnToggleOn != null) || (enableToggleOnOff == false), | |
'If you are enabling toggle On/Off plz provide backgroundColorOnTap', | |
), | |
assert( | |
(enableToggleOnOff && onToggleOff != null) || (enableToggleOnOff == false), | |
'If you are enabling toggle On/Off plz provide onToggleOff function', | |
); | |
final Widget buttonContent; | |
final Widget? buttonContentOnToggleOn; | |
final Function onToggleOn; | |
final Function? onToggleOff; | |
final bool enableToggleOnOff; | |
final Color backgroundColor; | |
final Color? backgroundColorOnToggleOn; | |
@override | |
Widget build(BuildContext context) { | |
final buttonBackgroundColor = useState(backgroundColor); | |
final isPressed = useState(false); | |
final child = useState(buttonContent); | |
void changeButtonContentToToggleOn() { | |
if (buttonContentOnToggleOn != null) { | |
child.value = buttonContentOnToggleOn!; | |
} | |
} | |
void changeButtonContentToToggleOff() { | |
child.value = buttonContent; | |
} | |
void runToggleOff() { | |
buttonBackgroundColor.value = backgroundColor; | |
isPressed.value = false; | |
changeButtonContentToToggleOff(); | |
onToggleOff!(); | |
} | |
void runToggleOn() { | |
buttonBackgroundColor.value = backgroundColorOnToggleOn!; | |
isPressed.value = true; | |
changeButtonContentToToggleOn(); | |
onToggleOn(); | |
} | |
void runOnOffAction() { | |
final isButtonPressed = isPressed.value; | |
isButtonPressed ? runToggleOff() : runToggleOn(); | |
} | |
void runOnOffActionOrOnTap() { | |
enableToggleOnOff ? runOnOffAction() : onToggleOn(); | |
} | |
return GestureDetector( | |
key: UniqueKey(), | |
onTap: () => runOnOffActionOrOnTap(), | |
child: Container( | |
height: Dim.d50, | |
decoration: BoxDecoration( | |
borderRadius: BorderRadius.circular(AppDimensions.xxRoundedBorderRadius), | |
border: Border.all( | |
color: backgroundColorOnToggleOn ?? backgroundColor, | |
width: Dim.d2, | |
), | |
color: buttonBackgroundColor.value, | |
), | |
child: Padding( | |
padding: const EdgeInsets.symmetric(vertical: Dim.d10, horizontal: Dim.d15), | |
child: Center(child: child.value), | |
), | |
), | |
); | |
} | |
} | |
//TESTS | |
const String _testText = 'Hello tests'; | |
const String _testOnText = 'Bye bye tests'; | |
const Color _testBackgroundColor = Colors.white; | |
const Color _testOnTapBackgroundColor = Colors.black; | |
void main() { | |
Widget testWidget({Function? onTestTap}) => MaterialApp( | |
home: Scaffold( | |
body: RepaintBoundary( | |
child: TextToggleOnOffButton( | |
buttonContent: const Text(_testText), | |
onToggleOn: onTestTap ?? () => print('Test button tapped'), | |
backgroundColor: _testBackgroundColor, | |
), | |
), | |
), | |
); | |
Widget testWidgetWithoutOnTapOffParam({Function? onTestTap}) => MaterialApp( | |
home: Scaffold( | |
body: RepaintBoundary( | |
child: TextToggleOnOffButton( | |
buttonContent: const Text(_testText), | |
onToggleOn: onTestTap ?? () => print('Test button tapped'), | |
backgroundColor: _testBackgroundColor, | |
backgroundColorOnToggleOn: _testOnTapBackgroundColor, | |
enableToggleOnOff: true, | |
), | |
), | |
), | |
); | |
Widget testWidgetWithoutBackgroundColorOnTapParam({Function? onTestTap}) => MaterialApp( | |
home: Scaffold( | |
body: RepaintBoundary( | |
child: TextToggleOnOffButton( | |
buttonContent: const Text(_testText), | |
onToggleOn: onTestTap ?? () => print('Test button tapped'), | |
backgroundColor: _testBackgroundColor, | |
enableToggleOnOff: true, | |
onToggleOff: () {}, | |
), | |
), | |
), | |
); | |
Widget testWidgetWithFullToggleOnOffParamsSet({ | |
Function? onTestToggleOn, | |
Function? onTestToggleOff, | |
bool? enableToggleOnOff, | |
}) => | |
MaterialApp( | |
home: Scaffold( | |
body: RepaintBoundary( | |
child: TextToggleOnOffButton( | |
buttonContent: const Text(_testText), | |
buttonContentOnToggleOn: const Text(_testOnText), | |
onToggleOn: onTestToggleOn ?? () => print('Test button ON'), | |
onToggleOff: onTestToggleOff ?? () => print('Test button OFF'), | |
enableToggleOnOff: enableToggleOnOff ?? true, | |
backgroundColor: _testBackgroundColor, | |
backgroundColorOnToggleOn: _testOnTapBackgroundColor, | |
), | |
), | |
), | |
); | |
group('Basic rendering', () { | |
testWidgets( | |
'Should render text', | |
(tester) async { | |
//Given | |
await tester.pumpWidget(testWidget()); | |
//Then | |
expect(find.text(_testText), findsOneWidget); | |
}, | |
); | |
testWidgets( | |
'Should render proper background color', | |
(tester) async { | |
//Given | |
await tester.pumpWidget(testWidget()); | |
final element = tester.element(find.byType(Container)); | |
final container = element.widget as Container; | |
final resultDecorationColor = (container.decoration as BoxDecoration).color; | |
//Then | |
expect(resultDecorationColor, _testBackgroundColor); | |
}, | |
); | |
}); | |
group('Assertions', () { | |
testWidgets( | |
'Should render with all params', | |
(tester) async { | |
//Given | |
await tester.pumpWidget(testWidgetWithFullToggleOnOffParamsSet()); | |
//Then | |
expect(find.text(_testText), findsOneWidget); | |
}, | |
); | |
testWidgets( | |
'Should throw exception when enableToggleOnOff and backgroundColorOnToggleOn is not provided ', | |
(tester) async { | |
expect( | |
//When | |
() async => await tester.pumpWidget(testWidgetWithoutBackgroundColorOnTapParam()), | |
//Then | |
throwsAssertionError, | |
); | |
}, | |
); | |
testWidgets( | |
'Should throw exception when enableToggleOnOff and onTapOff function is not provided ', | |
(tester) async { | |
expect( | |
//When | |
() async => await tester.pumpWidget(testWidgetWithoutOnTapOffParam()), | |
//Then | |
throwsAssertionError, | |
); | |
}, | |
); | |
}); | |
group('Background ON OFF state changes', () { | |
testWidgets( | |
'Should change background color after tap, toggle on', | |
(tester) async { | |
//Given | |
await tester.pumpWidget(testWidgetWithFullToggleOnOffParamsSet()); | |
//When | |
await tester.tap(find.byType(TextToggleOnOffButton)); | |
await tester.pump(); | |
//Then | |
final element = tester.element(find.byType(Container)); | |
final container = element.widget as Container; | |
final resultDecorationColor = (container.decoration as BoxDecoration).color; | |
expect(resultDecorationColor, _testOnTapBackgroundColor); | |
}, | |
); | |
testWidgets( | |
'Should change background color after tap, and go back to original after another tap', | |
(tester) async { | |
//Given | |
await tester.pumpWidget(testWidgetWithFullToggleOnOffParamsSet()); | |
//When | |
//Should change to _testOnTapBackgroundColor | |
await tester.tap(find.byType(TextToggleOnOffButton)); | |
//Should change back to _testBackgroundColor | |
await tester.tap(find.byType(TextToggleOnOffButton)); | |
await tester.pump(); | |
//Then | |
final element = tester.element(find.byType(Container)); | |
final container = element.widget as Container; | |
final resultDecorationColor = (container.decoration as BoxDecoration).color; | |
expect(resultDecorationColor, _testBackgroundColor); | |
}, | |
); | |
}); | |
group('Functions on and off', () { | |
testWidgets( | |
'Should run function on tap increasing counter to one', | |
(tester) async { | |
//Given | |
var testCounter = 0; | |
await tester.pumpWidget(testWidget(onTestTap: () => testCounter++)); | |
//When | |
await tester.tap(find.byType(TextToggleOnOffButton)); | |
//Then | |
expect(testCounter, 1); | |
}, | |
); | |
testWidgets( | |
'Should NOT run onTestToggleOff function while enableToggleOnOff option is false', | |
(tester) async { | |
//Given | |
var testCounter = 0; | |
await tester.pumpWidget( | |
testWidgetWithFullToggleOnOffParamsSet( | |
enableToggleOnOff: false, | |
onTestToggleOff: () => testCounter++, | |
), | |
); | |
//When | |
//First tap run onToggleOn | |
await tester.tap(find.byType(TextToggleOnOffButton)); | |
//Second tap run onToggleOff | |
await tester.tap(find.byType(TextToggleOnOffButton)); | |
//Then | |
expect(testCounter, 0); | |
}, | |
); | |
testWidgets( | |
'Should run ON and OFF function, increasing counter by 1 and then by 100', | |
(tester) async { | |
//Given | |
var testCounter = 0; | |
await tester.pumpWidget( | |
testWidgetWithFullToggleOnOffParamsSet( | |
onTestToggleOn: () => testCounter++, | |
onTestToggleOff: () => testCounter = testCounter + 100, | |
), | |
); | |
//When | |
//First tap run onToggleOn | |
await tester.tap(find.byType(TextToggleOnOffButton)); | |
//Second tap run onToggleOff | |
await tester.tap(find.byType(TextToggleOnOffButton)); | |
//Then | |
expect(testCounter, 101); | |
}, | |
); | |
}); | |
group('Content child ON OFF state changes', () { | |
testWidgets( | |
'Should change content after tap, toggle on', | |
(tester) async { | |
//Given | |
await tester.pumpWidget(testWidgetWithFullToggleOnOffParamsSet()); | |
//When | |
await tester.tap(find.byType(TextToggleOnOffButton)); | |
await tester.pump(); | |
//Then | |
expect(find.text(_testOnText), findsOneWidget); | |
}, | |
); | |
testWidgets( | |
'Should change content after tap, and go back to original after another tap', | |
(tester) async { | |
//Given | |
await tester.pumpWidget(testWidgetWithFullToggleOnOffParamsSet()); | |
//When | |
//Should change to _testOnText | |
await tester.tap(find.byType(TextToggleOnOffButton)); | |
//Should change back to _testText | |
await tester.tap(find.byType(TextToggleOnOffButton)); | |
await tester.pump(); | |
//Then | |
expect(find.text(_testText), findsOneWidget); | |
}, | |
); | |
}); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment