Skip to content

Commit

Permalink
feat: Dial Pad (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
thelukewalton committed Mar 1, 2024
1 parent d18dbd2 commit f16df26
Show file tree
Hide file tree
Showing 14 changed files with 606 additions and 102 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @benken @mikecoomber @thelukewalton
19 changes: 7 additions & 12 deletions example/lib/home.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:zeta_example/pages/components/bottom_sheet_example.dart';
import 'package:zeta_example/pages/components/button_example.dart';
import 'package:zeta_example/pages/components/checkbox_example.dart';
import 'package:zeta_example/pages/components/chip_example.dart';
import 'package:zeta_example/pages/components/dialpad_example.dart';
import 'package:zeta_example/pages/theme/color_example.dart';
import 'package:zeta_example/pages/components/password_input_example.dart';
import 'package:zeta_example/pages/components/progress_example.dart';
Expand All @@ -32,9 +33,9 @@ final List<Component> components = [
Component(ButtonExample.name, (context) => const ButtonExample()),
Component(CheckBoxExample.name, (context) => const CheckBoxExample()),
Component(ChipExample.name, (context) => const ChipExample()),
Component(
PasswordInputExample.name, (context) => const PasswordInputExample()),
Component(ProgressExample.name, (context) => const ProgressExample())
Component(PasswordInputExample.name, (context) => const PasswordInputExample()),
Component(ProgressExample.name, (context) => const ProgressExample()),
Component(DialPadExample.name, (context) => const DialPadExample()),
];

final List<Component> theme = [
Expand Down Expand Up @@ -96,27 +97,21 @@ class _HomeState extends State<Home> {
title: Text('Widgets'),
backgroundColor: Zeta.of(context).colors.warm.shade30,
children: _components
.map((item) => ListTile(
title: Text(item.name),
onTap: () => context.go('/${item.name}')))
.map((item) => ListTile(title: Text(item.name), onTap: () => context.go('/${item.name}')))
.toList(),
),
ExpansionTile(
title: Text('Theme'),
backgroundColor: Zeta.of(context).colors.warm.shade30,
children: _theme
.map((item) => ListTile(
title: Text(item.name),
onTap: () => context.go('/${item.name}')))
.map((item) => ListTile(title: Text(item.name), onTap: () => context.go('/${item.name}')))
.toList(),
),
ExpansionTile(
title: Text('Assets'),
backgroundColor: Zeta.of(context).colors.warm.shade30,
children: _assets
.map((item) => ListTile(
title: Text(item.name),
onTap: () => context.go('/${item.name}')))
.map((item) => ListTile(title: Text(item.name), onTap: () => context.go('/${item.name}')))
.toList(),
),
],
Expand Down
95 changes: 95 additions & 0 deletions example/lib/pages/components/dialpad_example.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import 'package:flutter/material.dart';
import 'package:zeta_example/widgets.dart';
import 'package:zeta_flutter/zeta_flutter.dart';

const double _paddingSize = 40;

class DialPadExample extends StatefulWidget {
static const String name = 'DialPad';

const DialPadExample({super.key});

@override
State<DialPadExample> createState() => _DialPadExampleState();
}

class _DialPadExampleState extends State<DialPadExample> {
String number = '', text = '';

@override
Widget build(BuildContext context) {
return ExampleScaffold(
name: DialPadExample.name,
child: LayoutBuilder(builder: (context, constraints) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: constraints.maxWidth,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const SizedBox(width: _paddingSize),
SizedBox(
width: constraints.maxWidth - (_paddingSize * 2),
child: Text(
number,
style: ZetaTextStyles.heading3,
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
),
),
IconButton(
icon: Icon(Icons.backspace),
onPressed: () => number.length == 0
? null
: setState(
() => number = number.substring(0, (number.length - 1)),
),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const SizedBox(),
Text(
text,
style: ZetaTextStyles.heading3,
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
),
IconButton(
icon: Icon(Icons.backspace),
onPressed: () => text.length == 0
? null
: setState(
() => text = text.substring(0, text.length - 1),
),
)
],
),
ZetaDialPad(
onNumber: (value) => setState(() => number += value),
onText: (value) => setState(() => text += value),
),
ZetaButton.primary(
label: 'Clear',
borderType: ZetaWidgetBorder.full,
onPressed: () => setState(() => number = text = ''),
)
].divide(const SizedBox(height: ZetaSpacing.m)).toList(),
),
),
],
);
}),
);
}
}
24 changes: 5 additions & 19 deletions example/lib/pages/components/progress_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,7 @@ class ProgressExampleState extends State<ProgressExample> {
SizedBox(
height: 20,
),
Wrapper(
stepsCompleted: 0,
type: ZetaBarType.standard,
isThin: false,
stateChangeable: true),
Wrapper(stepsCompleted: 0, type: ZetaBarType.standard, isThin: false, stateChangeable: true),
SizedBox(
height: 20,
),
Expand Down Expand Up @@ -94,9 +90,7 @@ class _WrapperState extends State<Wrapper> {

void setLoading() {
setState(() {
type = type == ZetaBarType.buffering
? ZetaBarType.standard
: ZetaBarType.buffering;
type = type == ZetaBarType.buffering ? ZetaBarType.standard : ZetaBarType.buffering;
});
}

Expand All @@ -108,25 +102,17 @@ class _WrapperState extends State<Wrapper> {
SizedBox(
width: 400,
child: ZetaProgressBar(
progress: progress,
rounded: widget.rounded,
type: type,
isThin: widget.isThin,
label: widget.label),
progress: progress, rounded: widget.rounded, type: type, isThin: widget.isThin, label: widget.label),
),
const SizedBox(width: 40),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
widget.type != ZetaBarType.indeterminate
? FilledButton(
onPressed: increasePercentage, child: Text("Increase"))
? FilledButton(onPressed: increasePercentage, child: Text("Increase"))
: Container(),
const SizedBox(width: 40),
widget.stateChangeable
? FilledButton(
onPressed: setLoading, child: Text("Start Buffering"))
: Container()
widget.stateChangeable ? FilledButton(onPressed: setLoading, child: Text("Start Buffering")) : Container()
],
)
],
Expand Down
116 changes: 116 additions & 0 deletions example/test/dialpad_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:zeta_flutter/zeta_flutter.dart';

import 'test_components.dart';

void main() {
group('ZetaDialPad Tests', () {
testWidgets('Initializes with correct parameters', (WidgetTester tester) async {
String number = '', text = '';

Future<void> debounceWait() => tester.binding.delayed(const Duration(milliseconds: 500));

await tester.pumpWidget(
TestWidget(
screenSize: Size(1000, 1000),
widget: ZetaDialPad(
onNumber: (value) => number += value,
onText: (value) => text += value,
),
),
);
final dialPadFinder = find.byType(ZetaDialPad);
final buttonFinder = find.byType(ZetaDialPadButton);

final oneFinder = find.byWidgetPredicate((widget) => widget is ZetaDialPadButton && widget.primary == '1');
final twoFinder = find.byWidgetPredicate((widget) => widget is ZetaDialPadButton && widget.primary == '2');
final threeFinder = find.byWidgetPredicate((widget) => widget is ZetaDialPadButton && widget.primary == '3');
final starFinder = find.byWidgetPredicate((widget) => widget is ZetaDialPadButton && widget.primary == '*');
final hashFinder = find.byWidgetPredicate((widget) => widget is ZetaDialPadButton && widget.primary == '#');

final ZetaDialPad dialPad = tester.firstWidget(dialPadFinder);
final List<Widget> dialPadButtons = tester.widgetList(buttonFinder).toList();

final ZetaDialPadButton one = tester.firstWidget(oneFinder);
final ZetaDialPadButton two = tester.firstWidget(twoFinder);
final ZetaDialPadButton three = tester.firstWidget(threeFinder);

/// Dial Pad built correctly.
expect(dialPad.buttonsPerRow, 3);
expect(dialPadButtons.length, 12);

/// Dial Pad buttons built correctly.
expect(one.primary, '1');
expect(one.secondary, '');
expect(two.primary, '2');
expect(two.secondary, 'ABC');
expect(three.primary, '3');
expect(three.secondary, 'DEF');

/// Tap button with only number.
await tester.tap(oneFinder);
await tester.pump();
expect(number, '1');

/// Tap button with number and text.
await tester.tap(twoFinder);
await tester.pump();
await debounceWait();
expect(number, '12');
expect(text, 'A');

/// Tap different button.
await tester.tap(threeFinder);
await tester.pump();
await debounceWait();
expect(number, '123');
expect(text, 'AD');

/// Tap text button twice.
await tester.tap(twoFinder);
await tester.tap(twoFinder);
await tester.pump();
await debounceWait();
expect(text, 'ADB');

/// Tap text button thrice.
await tester.tap(twoFinder);
await tester.tap(twoFinder);
await tester.tap(twoFinder);
await tester.pump();
await debounceWait();
expect(text, 'ADBC');

/// Tap different text buttons to ensure debounce is cancelled.
await tester.tap(twoFinder);
await tester.tap(threeFinder);
await tester.tap(twoFinder);
await tester.pump();
await debounceWait();
expect(text, 'ADBCADA');

/// Tap text button more times than there is options to ensure it loops around correctly.
await tester.tap(threeFinder);
await tester.tap(threeFinder);
await tester.tap(threeFinder);
await tester.tap(threeFinder);
await tester.tap(threeFinder);
await tester.tap(threeFinder);
await tester.tap(oneFinder);
await tester.pump();
expect(text, 'ADBCADAF');
number = '';

/// Tap buttons with symbols
await tester.ensureVisible(starFinder);
await tester.tap(starFinder);
await tester.tap(hashFinder);
await tester.pump();
expect(number, '*#');

/// Allow all timers to end in text debounce
await debounceWait();
});
});
}
17 changes: 10 additions & 7 deletions example/web/index.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<!DOCTYPE html>
<html>

<head>
<!--
If you are serving your web app in a path other than the root, change the
Expand All @@ -18,7 +19,7 @@

<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="Demonstrates how to use the zeta_flutter plugin.">
<meta name="description" content="Demonstrates how to use the zeta_flutter library.">

<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
Expand All @@ -27,33 +28,35 @@
<link rel="apple-touch-icon" href="icons/Icon-192.png">

<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<link rel="icon" type="image/png" href="favicon.png" />

<title>zeta_flutter_example</title>
<link rel="manifest" href="manifest.json">

<script>
// The value below is injected by flutter build, do not touch.
var serviceWorkerVersion = null;
const serviceWorkerVersion = null;
</script>
<!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script>
</head>

<body>
<script>
window.addEventListener('load', function(ev) {
window.addEventListener('load', function (ev) {
// Download main.dart.js
_flutter.loader.loadEntrypoint({
serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion,
},
onEntrypointLoaded: function(engineInitializer) {
engineInitializer.initializeEngine().then(function(appRunner) {
onEntrypointLoaded: function (engineInitializer) {
engineInitializer.initializeEngine({ useColorEmoji: true, }).then(function (appRunner) {
appRunner.runApp();
});
}
});
});
</script>
</body>
</html>

</html>
Loading

0 comments on commit f16df26

Please sign in to comment.