Skip to content

Commit

Permalink
Dial Pad buttons
Browse files Browse the repository at this point in the history
  • Loading branch information
thelukewalton committed Feb 28, 2024
1 parent e017809 commit dce40d7
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 8 deletions.
2 changes: 2 additions & 0 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/assets/icons_example.dart';
Expand All @@ -31,6 +32,7 @@ final List<Component> components = [
Component(CheckBoxExample.name, (context) => const CheckBoxExample()),
Component(ChipExample.name, (context) => const ChipExample()),
Component(PasswordInputExample.name, (context) => const PasswordInputExample()),
Component(DialPadExample.name, (context) => const DialPadExample()),
];

final List<Component> theme = [
Expand Down
27 changes: 27 additions & 0 deletions example/lib/pages/components/dialpad_example.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
import 'package:zeta_example/widgets.dart';
import 'package:zeta_flutter/zeta_flutter.dart';

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

const DialPadExample({super.key});

@override
Widget build(BuildContext context) {
return ExampleScaffold(
name: name,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ZetaDialPad(),
],
),
],
),
);
}
}
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>
45 changes: 45 additions & 0 deletions example/widgetbook/pages/components/dial_pad_widgetbook.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
import 'package:widgetbook/widgetbook.dart';
import 'package:zeta_flutter/zeta_flutter.dart';

import '../../test/test_components.dart';

WidgetbookComponent dialPadWidgetbook() {
return WidgetbookComponent(
isInitiallyExpanded: false,
name: 'Dial Pad',
useCases: [
WidgetbookUseCase(
name: 'Dial Pad',
builder: (context) {
return WidgetbookTestWidget(
widget: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ZetaDialPad(
buttonValues: context.knobs.boolean(label: 'Change to emoji')
? {
'😀': '',
'🥲': '',
'🥳': '',
'🤠': '',
'😨': '',
'👀': '',
'🐤': '',
'🐞': '',
'🦊': '',
'🏆': '',
'⛺️': '',
'🧽': ''
}
: null,
buttonsPerRow: context.knobs.int.slider(label: 'Buttons per row', initialValue: 3, min: 1, max: 9),
),
],
).paddingAll(ZetaSpacing.l),
);
},
),
],
);
}
2 changes: 2 additions & 0 deletions example/widgetbook/widgetbook.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'pages/components/badges_widgetbook.dart';
import 'pages/components/bottom_sheet_widgetbook.dart';
import 'pages/components/button_widgetbook.dart';
import 'pages/components/checkbox_widgetbook.dart';
import 'pages/components/dial_pad_widgetbook.dart';
import 'pages/theme/color_widgetbook.dart';
import 'pages/components/banner_widgetbook.dart';
import 'pages/components/chip_widgetbook.dart';
Expand Down Expand Up @@ -39,6 +40,7 @@ class HotReload extends StatelessWidget {
chipWidgetBook(),
passwordInputWidgetBook(),
bottomSheetWidgetBook(),
dialPadWidgetbook(),
]..sort((a, b) => a.name.compareTo(b.name)),
),
WidgetbookCategory(
Expand Down
168 changes: 168 additions & 0 deletions lib/src/components/dial_pad/dial_pad_buttons.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

import '../../../zeta_flutter.dart';

const Map<String, String> _defaultButtonValues = {
'1': '',
'2': 'ABC',
'3': 'DEF',
'4': 'GHI',
'5': 'JKL',
'6': 'MNO',
'7': 'PQRS',
'8': 'TUV',
'9': 'WXYZ',
'*': '',
'0': '+',
'#': '',
};

const int _defaultButtonsPerRow = 3;

/// Dial pad gives the user the ability to dial a number and start a call. It also has a quick dial security action and a delete entry action.
class ZetaDialPad extends StatelessWidget {
/// Constructs a [ZetaDialPad].
const ZetaDialPad({
super.key,
this.onInput,
this.buttonsPerRow = _defaultButtonsPerRow,
this.buttonValues = _defaultButtonValues,
});

/// Callback when number is tapped. Returns the large value from the button, i,e, 1,2,3 etc.
final ValueChanged<String>? onInput;

/// Number of buttons to show on each row. Defaults to 3.
final int? buttonsPerRow;

/// Map of values to show on the buttons.
///
/// Key is the large character, i.e. 1, 2, 3.
///
/// Value is the smaller character(s): i.e. 'ABC'
final Map<String, String>? buttonValues;

int get _buttonsPerRow => buttonsPerRow ?? _defaultButtonsPerRow;
Map<String, String> get _buttonValues => buttonValues ?? _defaultButtonValues;

@override
Widget build(BuildContext context) {
return SelectionContainer.disabled(
child: SizedBox(
width: (_buttonsPerRow * ZetaSpacing.x16) + ((_buttonsPerRow - 1) * ZetaSpacing.x9),
child: GridView.count(
crossAxisCount: _buttonsPerRow,
shrinkWrap: true,
semanticChildCount: _buttonValues.length,
mainAxisSpacing: ZetaSpacing.x9,
crossAxisSpacing: ZetaSpacing.x8,
children: _buttonValues.entries
.map(
(e) => _DialPadButton(
number: e.key,
letters: e.value,
onTap: onInput,
topPadding: e.key == '*'
? 11
: e.value.isEmpty && e.key != '1'
? 14
: 3,
),
)
.toList(),
),
),
);
}

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(ObjectFlagProperty<ValueChanged<String>?>.has('onInput', onInput))
..add(IntProperty('buttonsPerRow', buttonsPerRow))
..add(DiagnosticsProperty<Map<String, String>>('buttonValues', buttonValues));
}
}

class _DialPadButton extends StatefulWidget {
const _DialPadButton({
required this.number,
this.letters = '',
required this.onTap,
this.topPadding = 3,
});

final String number;
final String letters;
final double topPadding;
final ValueChanged<String>? onTap;

@override
State<_DialPadButton> createState() => _DialPadButtonState();

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(ObjectFlagProperty<ValueChanged<String>>.has('onTap', onTap))
..add(StringProperty('letters', letters))
..add(StringProperty('number', number))
..add(DoubleProperty('topPadding', topPadding));
}
}

class _DialPadButtonState extends State<_DialPadButton> {
bool _focused = false;

@override
Widget build(BuildContext context) {
final colors = Zeta.of(context).colors;

return Semantics(
button: true,
value: widget.number,
excludeSemantics: true,
child: AnimatedContainer(
duration: Durations.short2,
width: ZetaSpacing.x16,
height: ZetaSpacing.x16,
decoration: ShapeDecoration(
shape: CircleBorder(
side: _focused ? BorderSide(color: colors.blue, width: ZetaSpacing.x0_5) : BorderSide.none,
),
color: colors.warm.shade10,
shadows: [BoxShadow(color: colors.black.withOpacity(0.15), blurRadius: 4, offset: const Offset(0, 2))],
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: () => widget.onTap?.call(widget.number),
borderRadius: ZetaRadius.full,
overlayColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.pressed)) {
return colors.surfaceSelectedHovered;
}
if (states.contains(MaterialState.hovered)) {
return colors.surfaceHovered;
}
return null;
}),
onFocusChange: (value) {
if (_focused != value) setState(() => _focused = value);
},
child: Column(
children: [
SizedBox(height: widget.topPadding),
Text(widget.number, style: ZetaTextStyles.heading1),
if (widget.topPadding < 10) Text(widget.letters, style: ZetaTextStyles.labelIndicator),
],
),
),
),
),
);
}
}
2 changes: 1 addition & 1 deletion lib/src/theme/typography.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class ZetaTextStyles {
/// Headline styles are smaller than display styles. They're best-suited for
/// short, high-emphasis text on smaller screens.
/// {@endtemplate}
static const TextStyle heading1 = TextStyle(fontSize: 32, fontWeight: FontWeight.w500, height: 40 / 32);
static const TextStyle heading1 = TextStyle(fontSize: 32, fontWeight: FontWeight.w500, height: 36 / 32);

/// Middle size of the headline styles.
///
Expand Down
1 change: 1 addition & 0 deletions lib/zeta_flutter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export 'src/components/buttons/fab.dart';
export 'src/components/buttons/icon_button.dart';
export 'src/components/checkbox/checkbox.dart';
export 'src/components/chips/chip.dart';
export 'src/components/dial_pad/dial_pad_buttons.dart';
export 'src/components/password/password_input.dart';
export 'src/theme/color_extensions.dart';
export 'src/theme/color_scheme.dart';
Expand Down

0 comments on commit dce40d7

Please sign in to comment.