@@ -11,150 +11,73 @@ void main() {
1111 runApp (
1212 const MaterialApp (
1313 home: Scaffold (
14- body: Center (child: SimpleUSPhoneNumberEntry ()),
14+ body: Center (child: VerificationCodeGenerator ()),
1515 ),
1616 ),
1717 );
1818}
1919
20- // This implements a custom phone number input field that handles the
21- // [DeleteCharacterIntent] intent.
22- class DigitInput extends StatefulWidget {
23- const DigitInput ({
24- super .key,
25- required this .controller,
26- required this .focusNode,
27- this .maxLength,
28- this .textInputAction = TextInputAction .next,
29- });
20+ const CopyTextIntent copyTextIntent = CopyTextIntent ._();
21+ class CopyTextIntent extends Intent {
22+ const CopyTextIntent ._();
23+ }
3024
31- final int ? maxLength;
32- final TextEditingController controller;
33- final TextInputAction textInputAction;
34- final FocusNode focusNode;
25+ class CopyableText extends StatelessWidget {
26+ const CopyableText ({ super .key, required this .text });
3527
36- @override
37- DigitInputState createState () => DigitInputState ();
38- }
28+ final String text;
3929
40- class DigitInputState extends State <DigitInput > {
41- late final Action <DeleteCharacterIntent > _deleteTextAction =
42- CallbackAction <DeleteCharacterIntent >(
43- onInvoke: (DeleteCharacterIntent intent) {
44- // For simplicity we delete everything in the section.
45- widget.controller.clear ();
46- return null ;
47- },
48- );
30+ void _copy (CopyTextIntent intent) => Clipboard .setData (ClipboardData (text: text));
4931
5032 @override
5133 Widget build (BuildContext context) {
52- return Actions (
53- actions: < Type , Action <Intent >> {
54- // Make the default `DeleteCharacterIntent` handler overridable.
55- DeleteCharacterIntent : Action <DeleteCharacterIntent >.overridable (
56- defaultAction: _deleteTextAction, context: context),
57- },
58- child: TextField (
59- controller: widget.controller,
60- textInputAction: TextInputAction .next,
61- keyboardType: TextInputType .phone,
62- focusNode: widget.focusNode,
63- decoration: const InputDecoration (
64- border: OutlineInputBorder (),
34+ final Action <CopyTextIntent > defaultCopyAction = CallbackAction <CopyTextIntent >(onInvoke: _copy);
35+ return Shortcuts (
36+ shortcuts: const < ShortcutActivator , Intent > { SingleActivator (LogicalKeyboardKey .keyC, control: true ) : copyTextIntent },
37+ child: Actions (
38+ actions: < Type , Action <Intent >> {
39+ /// The Action is made overridable so the VerificationCodeGenerator
40+ /// widget can override how copying is handled.
41+ CopyTextIntent : Action <CopyTextIntent >.overridable (defaultAction: defaultCopyAction, context: context),
42+ },
43+ child: Focus (
44+ autofocus: true ,
45+ child: DefaultTextStyle .merge (
46+ style: const TextStyle (fontSize: 20 , fontWeight: FontWeight .bold),
47+ child: Text (text),
48+ ),
6549 ),
66- inputFormatters: < TextInputFormatter > [
67- FilteringTextInputFormatter .digitsOnly,
68- LengthLimitingTextInputFormatter (widget.maxLength),
69- ],
7050 ),
7151 );
7252 }
7353}
7454
75- class SimpleUSPhoneNumberEntry extends StatefulWidget {
76- const SimpleUSPhoneNumberEntry ({super .key});
77-
78- @override
79- State <SimpleUSPhoneNumberEntry > createState () =>
80- _SimpleUSPhoneNumberEntryState ();
81- }
82-
83- class _DeleteDigit extends Action <DeleteCharacterIntent > {
84- _DeleteDigit (this .state);
55+ class VerificationCodeGenerator extends StatelessWidget {
56+ const VerificationCodeGenerator ({ super .key });
8557
86- final _SimpleUSPhoneNumberEntryState state;
87- @override
88- void invoke (DeleteCharacterIntent intent) {
89- assert (callingAction != null );
90- callingAction? .invoke (intent);
91-
92- if (state.lineNumberController.text.isEmpty &&
93- state.lineNumberFocusNode.hasFocus) {
94- state.prefixFocusNode.requestFocus ();
95- }
96-
97- if (state.prefixController.text.isEmpty && state.prefixFocusNode.hasFocus) {
98- state.areaCodeFocusNode.requestFocus ();
99- }
58+ void _copy (CopyTextIntent intent) {
59+ debugPrint ('Content copied' );
60+ Clipboard .setData (const ClipboardData (text: '111222333' ));
10061 }
10162
102- // This action is only enabled when the `callingAction` exists and is
103- // enabled.
104- @override
105- bool get isActionEnabled => callingAction? .isActionEnabled ?? false ;
106- }
107-
108- class _SimpleUSPhoneNumberEntryState extends State <SimpleUSPhoneNumberEntry > {
109- final FocusNode areaCodeFocusNode = FocusNode ();
110- final TextEditingController areaCodeController = TextEditingController ();
111- final FocusNode prefixFocusNode = FocusNode ();
112- final TextEditingController prefixController = TextEditingController ();
113- final FocusNode lineNumberFocusNode = FocusNode ();
114- final TextEditingController lineNumberController = TextEditingController ();
115-
11663 @override
11764 Widget build (BuildContext context) {
11865 return Actions (
119- actions: < Type , Action <Intent >> {
120- DeleteCharacterIntent : _DeleteDigit (this ),
121- },
122- child: Row (
123- mainAxisAlignment: MainAxisAlignment .spaceBetween,
66+ actions: < Type , Action <Intent >> { CopyTextIntent : CallbackAction <CopyTextIntent >(onInvoke: _copy) },
67+ child: Column (
68+ mainAxisAlignment: MainAxisAlignment .center,
12469 children: < Widget > [
125- const Expanded (
126- child: Text ('(' , textAlign: TextAlign .center),
127- ),
128- Expanded (
129- flex: 3 ,
130- child: DigitInput (
131- focusNode: areaCodeFocusNode,
132- controller: areaCodeController,
133- maxLength: 3 ,
134- ),
135- ),
136- const Expanded (
137- child: Text (')' , textAlign: TextAlign .center),
138- ),
139- Expanded (
140- flex: 3 ,
141- child: DigitInput (
142- focusNode: prefixFocusNode,
143- controller: prefixController,
144- maxLength: 3 ,
145- ),
146- ),
147- const Expanded (
148- child: Text ('-' , textAlign: TextAlign .center),
149- ),
150- Expanded (
151- flex: 4 ,
152- child: DigitInput (
153- focusNode: lineNumberFocusNode,
154- controller: lineNumberController,
155- textInputAction: TextInputAction .done,
156- maxLength: 4 ,
157- ),
70+ const Text ('Press Ctrl-C to Copy' ),
71+ const SizedBox (height: 10 ),
72+ Row (
73+ mainAxisAlignment: MainAxisAlignment .center,
74+ children: const < Widget > [
75+ CopyableText (text: '111' ),
76+ SizedBox (width: 5 ,),
77+ CopyableText (text: '222' ),
78+ SizedBox (width: 5 ,),
79+ CopyableText (text: '333' ),
80+ ],
15881 ),
15982 ],
16083 ),
0 commit comments