blob: 5d8285239a8cf70db1d6ff500b6f41ca99b44078 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/painting.dart';
import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';
abstract class _KeyboardKey extends StatelessWidget {
const _KeyboardKey({
@required this.height,
@required this.onTap,
this.onLongPress,
this.flex = 2,
Key key,
}) : super(key: key);
/// The size of the key relative to its siblings.
final int flex;
/// The height of the key.
final double height;
/// Called when the key is pressed.
final Function onTap;
/// Called when the key is long-pressed.
final VoidCallback onLongPress;
}
/// Called when a key is pressed.
typedef OnText = void Function(String text);
/// A spacer that is inserted into the keyboard to provide empty space
/// between other keys in the keyboard.
///
/// The [SpacerKey] is expected to have a [Row] as a parent.
///
/// Note: No state is required but necessary in order to subclass [_KeyboardKey].
class SpacerKey extends _KeyboardKey {
const SpacerKey({
GlobalKey key,
int flex,
}) : super(key: key, height: 0.0, flex: flex, onTap: null);
@override
Widget build(BuildContext context) => Spacer(flex: flex, key: key);
}
/// A key that is represented by a string.
///
/// The [TextKey] is expected to have a [Row] as a parent and an ancestor
/// [Material] widget
class TextKey extends _KeyboardKey {
const TextKey(
this.text, {
@required double height,
GlobalKey key,
OnText onText,
this.style,
this.verticalAlign = 0.5,
this.horizontalAlign = 0.5,
int flex,
}) : super(key: key, height: height, flex: flex, onTap: onText);
/// The text to display.
final String text;
/// The style of the text.
final TextStyle style;
/// The vertical alignment the text should have within its container.
final double verticalAlign;
/// The horizontal alignment the text should have within its container.
final double horizontalAlign;
@override
Widget build(BuildContext context) {
debugCheckHasMaterial(context);
_debugCheckHasRow(context);
return Expanded(
flex: flex,
child: InkWell(
child: Container(
height: height,
child: Align(
alignment: FractionalOffset(horizontalAlign, verticalAlign),
child: Text(text, style: style),
),
),
onTap: () => onTap?.call(text),
onLongPress: onLongPress,
),
);
}
}
enum IconKeyType {
normal,
bordered,
dark,
}
/// A key that is represented by an icon.
///
/// The [IconKey] is expected to have a [Row] as a parent and an ancestor
/// [Material] widget
class IconKey extends _KeyboardKey {
const IconKey({
@required this.iconData,
@required this.iconColor,
@required double height,
this.type = IconKeyType.normal,
this.accentColor = Colors.transparent,
VoidCallback onKeyPressed,
int flex,
Key key,
}) : super(height: height, flex: flex, key: key, onTap: onKeyPressed);
static const double _padding = 4.0;
static const double _borderWidth = 2.0;
/// The [IconData] of the key
final IconData iconData;
final IconKeyType type;
/// The color filter to apply to the icon in the key.
final Color iconColor;
/// The color filter to the background or border if [type] is
/// not [IconKeyType.normal].
final Color accentColor;
@override
Widget build(BuildContext context) {
debugCheckHasMaterial(context);
_debugCheckHasRow(context);
final shape = CircleBorder(
side: type == IconKeyType.bordered
? BorderSide(color: accentColor, width: _borderWidth)
: BorderSide.none);
final backgroundColor =
type == IconKeyType.dark ? accentColor : Colors.transparent;
final padding = type == IconKeyType.dark ? _padding : 0.0;
return Expanded(
flex: flex,
child: Container(
padding: EdgeInsets.all(padding),
height: height,
child: Container(
decoration: ShapeDecoration(
color: backgroundColor,
shape: shape,
),
child: InkWell(
customBorder: type == IconKeyType.normal ? null : shape,
child: Icon(iconData, color: iconColor),
onTap: onTap ?? () {},
onLongPress: onLongPress,
),
),
),
);
}
}
/// A key that is representative of the space bar.
///
/// The [SpaceBarKey] is expected to have a [Row] as a parent and an ancestor
/// [Material] widget
class SpaceBarKey extends _KeyboardKey {
const SpaceBarKey({
@required this.accentColor,
@required double height,
VoidCallback onTap,
int flex,
Key key,
}) : super(key: key, height: height, flex: flex, onTap: onTap);
/// The color filter to apply to the icon in the key.
final Color accentColor;
@override
Widget build(BuildContext context) {
debugCheckHasMaterial(context);
_debugCheckHasRow(context);
return Expanded(
flex: flex,
child: InkWell(
child: Container(
height: height,
child: FractionallySizedBox(
widthFactor: 0.9,
heightFactor: 0.2,
child: Container(
decoration: ShapeDecoration(
color: accentColor,
shape: StadiumBorder(side: BorderSide.none),
),
),
),
),
onTap: onTap ?? () {},
onLongPress: onLongPress,
),
);
}
}
bool _debugCheckHasRow(BuildContext context) {
assert(
context.widget is Row || context.ancestorWidgetOfExactType(Row) != null,
'${context.widget.runtimeType} widgets require a Row widget ancestor.');
return true;
}