mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
164 lines
4.7 KiB
Dart
164 lines
4.7 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// Flutter code sample for Action.Action.overridable
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
|
|
void main() {
|
|
runApp(
|
|
const MaterialApp(
|
|
home: Scaffold(
|
|
body: Center(child: SimpleUSPhoneNumberEntry()),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// This implements a custom phone number input field that handles the
|
|
// [DeleteCharacterIntent] intent.
|
|
class DigitInput extends StatefulWidget {
|
|
const DigitInput({
|
|
super.key,
|
|
required this.controller,
|
|
required this.focusNode,
|
|
this.maxLength,
|
|
this.textInputAction = TextInputAction.next,
|
|
});
|
|
|
|
final int? maxLength;
|
|
final TextEditingController controller;
|
|
final TextInputAction textInputAction;
|
|
final FocusNode focusNode;
|
|
|
|
@override
|
|
DigitInputState createState() => DigitInputState();
|
|
}
|
|
|
|
class DigitInputState extends State<DigitInput> {
|
|
late final Action<DeleteCharacterIntent> _deleteTextAction =
|
|
CallbackAction<DeleteCharacterIntent>(
|
|
onInvoke: (DeleteCharacterIntent intent) {
|
|
// For simplicity we delete everything in the section.
|
|
widget.controller.clear();
|
|
return null;
|
|
},
|
|
);
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Actions(
|
|
actions: <Type, Action<Intent>>{
|
|
// Make the default `DeleteCharacterIntent` handler overridable.
|
|
DeleteCharacterIntent: Action<DeleteCharacterIntent>.overridable(
|
|
defaultAction: _deleteTextAction, context: context),
|
|
},
|
|
child: TextField(
|
|
controller: widget.controller,
|
|
textInputAction: TextInputAction.next,
|
|
keyboardType: TextInputType.phone,
|
|
focusNode: widget.focusNode,
|
|
decoration: const InputDecoration(
|
|
border: OutlineInputBorder(),
|
|
),
|
|
inputFormatters: <TextInputFormatter>[
|
|
FilteringTextInputFormatter.digitsOnly,
|
|
LengthLimitingTextInputFormatter(widget.maxLength),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class SimpleUSPhoneNumberEntry extends StatefulWidget {
|
|
const SimpleUSPhoneNumberEntry({super.key});
|
|
|
|
@override
|
|
State<SimpleUSPhoneNumberEntry> createState() =>
|
|
_SimpleUSPhoneNumberEntryState();
|
|
}
|
|
|
|
class _DeleteDigit extends Action<DeleteCharacterIntent> {
|
|
_DeleteDigit(this.state);
|
|
|
|
final _SimpleUSPhoneNumberEntryState state;
|
|
@override
|
|
void invoke(DeleteCharacterIntent intent) {
|
|
assert(callingAction != null);
|
|
callingAction?.invoke(intent);
|
|
|
|
if (state.lineNumberController.text.isEmpty &&
|
|
state.lineNumberFocusNode.hasFocus) {
|
|
state.prefixFocusNode.requestFocus();
|
|
}
|
|
|
|
if (state.prefixController.text.isEmpty && state.prefixFocusNode.hasFocus) {
|
|
state.areaCodeFocusNode.requestFocus();
|
|
}
|
|
}
|
|
|
|
// This action is only enabled when the `callingAction` exists and is
|
|
// enabled.
|
|
@override
|
|
bool get isActionEnabled => callingAction?.isActionEnabled ?? false;
|
|
}
|
|
|
|
class _SimpleUSPhoneNumberEntryState extends State<SimpleUSPhoneNumberEntry> {
|
|
final FocusNode areaCodeFocusNode = FocusNode();
|
|
final TextEditingController areaCodeController = TextEditingController();
|
|
final FocusNode prefixFocusNode = FocusNode();
|
|
final TextEditingController prefixController = TextEditingController();
|
|
final FocusNode lineNumberFocusNode = FocusNode();
|
|
final TextEditingController lineNumberController = TextEditingController();
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Actions(
|
|
actions: <Type, Action<Intent>>{
|
|
DeleteCharacterIntent: _DeleteDigit(this),
|
|
},
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: <Widget>[
|
|
const Expanded(
|
|
child: Text('(', textAlign: TextAlign.center),
|
|
),
|
|
Expanded(
|
|
flex: 3,
|
|
child: DigitInput(
|
|
focusNode: areaCodeFocusNode,
|
|
controller: areaCodeController,
|
|
maxLength: 3,
|
|
),
|
|
),
|
|
const Expanded(
|
|
child: Text(')', textAlign: TextAlign.center),
|
|
),
|
|
Expanded(
|
|
flex: 3,
|
|
child: DigitInput(
|
|
focusNode: prefixFocusNode,
|
|
controller: prefixController,
|
|
maxLength: 3,
|
|
),
|
|
),
|
|
const Expanded(
|
|
child: Text('-', textAlign: TextAlign.center),
|
|
),
|
|
Expanded(
|
|
flex: 4,
|
|
child: DigitInput(
|
|
focusNode: lineNumberFocusNode,
|
|
controller: lineNumberController,
|
|
textInputAction: TextInputAction.done,
|
|
maxLength: 4,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|