Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
37ddad61
Unverified
Commit
37ddad61
authored
Aug 14, 2020
by
Michael Goderbauer
Committed by
GitHub
Aug 14, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Make textfields restorable (#63401)
parent
03dcd1bd
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
500 additions
and
124 deletions
+500
-124
text_field.dart
packages/flutter/lib/src/cupertino/text_field.dart
+94
-60
text_field.dart
packages/flutter/lib/src/material/text_field.dart
+114
-61
editable_text.dart
packages/flutter/lib/src/widgets/editable_text.dart
+21
-0
restoration_properties.dart
packages/flutter/lib/src/widgets/restoration_properties.dart
+20
-3
text_field_restoration_test.dart
...s/flutter/test/cupertino/text_field_restoration_test.dart
+126
-0
text_field_restoration_test.dart
...es/flutter/test/material/text_field_restoration_test.dart
+125
-0
No files found.
packages/flutter/lib/src/cupertino/text_field.dart
View file @
37ddad61
...
...
@@ -273,6 +273,7 @@ class CupertinoTextField extends StatefulWidget {
this
.
scrollController
,
this
.
scrollPhysics
,
this
.
autofillHints
,
this
.
restorationId
,
})
:
assert
(
textAlign
!=
null
),
assert
(
readOnly
!=
null
),
assert
(
autofocus
!=
null
),
...
...
@@ -600,6 +601,9 @@ class CupertinoTextField extends StatefulWidget {
/// {@macro flutter.services.autofill.autofillHints}
final
Iterable
<
String
>
autofillHints
;
/// {@macro flutter.material.textfield.restorationId}
final
String
restorationId
;
@override
_CupertinoTextFieldState
createState
()
=>
_CupertinoTextFieldState
();
...
...
@@ -641,11 +645,11 @@ class CupertinoTextField extends StatefulWidget {
}
}
class
_CupertinoTextFieldState
extends
State
<
CupertinoTextField
>
with
AutomaticKeepAliveClientMixin
implements
TextSelectionGestureDetectorBuilderDelegate
{
class
_CupertinoTextFieldState
extends
State
<
CupertinoTextField
>
with
RestorationMixin
,
AutomaticKeepAliveClientMixin
implements
TextSelectionGestureDetectorBuilderDelegate
{
final
GlobalKey
_clearGlobalKey
=
GlobalKey
();
TextEditingController
_controller
;
TextEditingController
get
_effectiveController
=>
widget
.
controller
??
_controller
;
Restorable
TextEditingController
_controller
;
TextEditingController
get
_effectiveController
=>
widget
.
controller
??
_controller
.
value
;
FocusNode
_focusNode
;
FocusNode
get
_effectiveFocusNode
=>
widget
.
focusNode
??
(
_focusNode
??=
FocusNode
());
...
...
@@ -670,8 +674,7 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
super
.
initState
();
_selectionGestureDetectorBuilder
=
_CupertinoTextFieldSelectionGestureDetectorBuilder
(
state:
this
);
if
(
widget
.
controller
==
null
)
{
_controller
=
TextEditingController
();
_controller
.
addListener
(
updateKeepAlive
);
_createLocalController
();
}
}
...
...
@@ -679,9 +682,10 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
void
didUpdateWidget
(
CupertinoTextField
oldWidget
)
{
super
.
didUpdateWidget
(
oldWidget
);
if
(
widget
.
controller
==
null
&&
oldWidget
.
controller
!=
null
)
{
_controller
=
TextEditingController
.
fromValue
(
oldWidget
.
controller
.
value
);
_controller
.
addListener
(
updateKeepAlive
);
_createLocalController
(
oldWidget
.
controller
.
value
);
}
else
if
(
widget
.
controller
!=
null
&&
oldWidget
.
controller
==
null
)
{
unregisterFromRestoration
(
_controller
);
_controller
.
dispose
();
_controller
=
null
;
}
final
bool
isEnabled
=
widget
.
enabled
??
true
;
...
...
@@ -691,10 +695,36 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
}
}
@override
void
restoreState
(
RestorationBucket
oldBucket
,
bool
initialRestore
)
{
if
(
_controller
!=
null
)
{
_registerController
();
}
}
void
_registerController
()
{
assert
(
_controller
!=
null
);
registerForRestoration
(
_controller
,
'controller'
);
_controller
.
value
.
addListener
(
updateKeepAlive
);
}
void
_createLocalController
([
TextEditingValue
value
])
{
assert
(
_controller
==
null
);
_controller
=
value
==
null
?
RestorableTextEditingController
()
:
RestorableTextEditingController
.
fromValue
(
value
);
if
(!
restorePending
)
{
_registerController
();
}
}
@override
String
get
restorationId
=>
widget
.
restorationId
;
@override
void
dispose
()
{
_focusNode
?.
dispose
();
_controller
?.
removeListener
(
updateKeepAlive
);
_controller
?.
dispose
(
);
super
.
dispose
();
}
...
...
@@ -736,7 +766,7 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
}
@override
bool
get
wantKeepAlive
=>
_controller
?.
text
?.
isNotEmpty
==
true
;
bool
get
wantKeepAlive
=>
_controller
?.
value
?.
text
?.
isNotEmpty
==
true
;
bool
_shouldShowAttachment
({
OverlayVisibilityMode
attachment
,
...
...
@@ -927,57 +957,61 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
final
Widget
paddedEditable
=
Padding
(
padding:
widget
.
padding
,
child:
RepaintBoundary
(
child:
EditableText
(
key:
editableTextKey
,
controller:
controller
,
readOnly:
widget
.
readOnly
,
toolbarOptions:
widget
.
toolbarOptions
,
showCursor:
widget
.
showCursor
,
showSelectionHandles:
_showSelectionHandles
,
focusNode:
_effectiveFocusNode
,
keyboardType:
widget
.
keyboardType
,
textInputAction:
widget
.
textInputAction
,
textCapitalization:
widget
.
textCapitalization
,
style:
textStyle
,
strutStyle:
widget
.
strutStyle
,
textAlign:
widget
.
textAlign
,
autofocus:
widget
.
autofocus
,
obscuringCharacter:
widget
.
obscuringCharacter
,
obscureText:
widget
.
obscureText
,
autocorrect:
widget
.
autocorrect
,
smartDashesType:
widget
.
smartDashesType
,
smartQuotesType:
widget
.
smartQuotesType
,
enableSuggestions:
widget
.
enableSuggestions
,
maxLines:
widget
.
maxLines
,
minLines:
widget
.
minLines
,
expands:
widget
.
expands
,
selectionColor:
selectionColor
,
selectionControls:
widget
.
selectionEnabled
?
cupertinoTextSelectionControls
:
null
,
onChanged:
widget
.
onChanged
,
onSelectionChanged:
_handleSelectionChanged
,
onEditingComplete:
widget
.
onEditingComplete
,
onSubmitted:
widget
.
onSubmitted
,
inputFormatters:
formatters
,
rendererIgnoresPointer:
true
,
cursorWidth:
widget
.
cursorWidth
,
cursorHeight:
widget
.
cursorHeight
,
cursorRadius:
widget
.
cursorRadius
,
cursorColor:
cursorColor
,
cursorOpacityAnimates:
true
,
cursorOffset:
cursorOffset
,
paintCursorAboveText:
true
,
autocorrectionTextRectColor:
selectionColor
,
backgroundCursorColor:
CupertinoDynamicColor
.
resolve
(
CupertinoColors
.
inactiveGray
,
context
),
selectionHeightStyle:
widget
.
selectionHeightStyle
,
selectionWidthStyle:
widget
.
selectionWidthStyle
,
scrollPadding:
widget
.
scrollPadding
,
keyboardAppearance:
keyboardAppearance
,
dragStartBehavior:
widget
.
dragStartBehavior
,
scrollController:
widget
.
scrollController
,
scrollPhysics:
widget
.
scrollPhysics
,
enableInteractiveSelection:
widget
.
enableInteractiveSelection
,
autofillHints:
widget
.
autofillHints
,
child:
UnmanagedRestorationScope
(
bucket:
bucket
,
child:
EditableText
(
key:
editableTextKey
,
controller:
controller
,
readOnly:
widget
.
readOnly
,
toolbarOptions:
widget
.
toolbarOptions
,
showCursor:
widget
.
showCursor
,
showSelectionHandles:
_showSelectionHandles
,
focusNode:
_effectiveFocusNode
,
keyboardType:
widget
.
keyboardType
,
textInputAction:
widget
.
textInputAction
,
textCapitalization:
widget
.
textCapitalization
,
style:
textStyle
,
strutStyle:
widget
.
strutStyle
,
textAlign:
widget
.
textAlign
,
autofocus:
widget
.
autofocus
,
obscuringCharacter:
widget
.
obscuringCharacter
,
obscureText:
widget
.
obscureText
,
autocorrect:
widget
.
autocorrect
,
smartDashesType:
widget
.
smartDashesType
,
smartQuotesType:
widget
.
smartQuotesType
,
enableSuggestions:
widget
.
enableSuggestions
,
maxLines:
widget
.
maxLines
,
minLines:
widget
.
minLines
,
expands:
widget
.
expands
,
selectionColor:
selectionColor
,
selectionControls:
widget
.
selectionEnabled
?
cupertinoTextSelectionControls
:
null
,
onChanged:
widget
.
onChanged
,
onSelectionChanged:
_handleSelectionChanged
,
onEditingComplete:
widget
.
onEditingComplete
,
onSubmitted:
widget
.
onSubmitted
,
inputFormatters:
formatters
,
rendererIgnoresPointer:
true
,
cursorWidth:
widget
.
cursorWidth
,
cursorHeight:
widget
.
cursorHeight
,
cursorRadius:
widget
.
cursorRadius
,
cursorColor:
cursorColor
,
cursorOpacityAnimates:
true
,
cursorOffset:
cursorOffset
,
paintCursorAboveText:
true
,
autocorrectionTextRectColor:
selectionColor
,
backgroundCursorColor:
CupertinoDynamicColor
.
resolve
(
CupertinoColors
.
inactiveGray
,
context
),
selectionHeightStyle:
widget
.
selectionHeightStyle
,
selectionWidthStyle:
widget
.
selectionWidthStyle
,
scrollPadding:
widget
.
scrollPadding
,
keyboardAppearance:
keyboardAppearance
,
dragStartBehavior:
widget
.
dragStartBehavior
,
scrollController:
widget
.
scrollController
,
scrollPhysics:
widget
.
scrollPhysics
,
enableInteractiveSelection:
widget
.
enableInteractiveSelection
,
autofillHints:
widget
.
autofillHints
,
restorationId:
'editable'
,
),
),
),
);
...
...
packages/flutter/lib/src/material/text_field.dart
View file @
37ddad61
...
...
@@ -372,6 +372,7 @@ class TextField extends StatefulWidget {
this
.
scrollController
,
this
.
scrollPhysics
,
this
.
autofillHints
,
this
.
restorationId
,
})
:
assert
(
textAlign
!=
null
),
assert
(
readOnly
!=
null
),
assert
(
autofocus
!=
null
),
...
...
@@ -787,6 +788,25 @@ class TextField extends StatefulWidget {
/// {@macro flutter.services.autofill.autofillHints}
final
Iterable
<
String
>
autofillHints
;
/// {@template flutter.material.textfield.restorationId}
/// Restoration ID to save and restore the state of the text field.
///
/// If non-null, the text field will persist and restore its current scroll
/// offset and - if no [controller] has been provided - the content of the
/// text field. If a [controller] has been provided, it is the responsibility
/// of the owner of that controller to persist and restore it, e.g. by using
/// a [RestorableTextEditingController].
///
/// The state of this widget is persisted in a [RestorationBucket] claimed
/// from the surrounding [RestorationScope] using the provided restoration ID.
///
/// See also:
///
/// * [RestorationManager], which explains how state restoration works in
/// Flutter.
/// {@endtemplate}
final
String
restorationId
;
@override
_TextFieldState
createState
()
=>
_TextFieldState
();
...
...
@@ -828,9 +848,9 @@ class TextField extends StatefulWidget {
}
}
class
_TextFieldState
extends
State
<
TextField
>
implements
TextSelectionGestureDetectorBuilderDelegate
{
TextEditingController
_controller
;
TextEditingController
get
_effectiveController
=>
widget
.
controller
??
_controller
;
class
_TextFieldState
extends
State
<
TextField
>
with
RestorationMixin
implements
TextSelectionGestureDetectorBuilderDelegate
{
Restorable
TextEditingController
_controller
;
TextEditingController
get
_effectiveController
=>
widget
.
controller
??
_controller
.
value
;
FocusNode
_focusNode
;
FocusNode
get
_effectiveFocusNode
=>
widget
.
focusNode
??
(
_focusNode
??=
FocusNode
());
...
...
@@ -937,7 +957,7 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
super
.
initState
();
_selectionGestureDetectorBuilder
=
_TextFieldSelectionGestureDetectorBuilder
(
state:
this
);
if
(
widget
.
controller
==
null
)
{
_c
ontroller
=
TextEditing
Controller
();
_c
reateLocal
Controller
();
}
_effectiveFocusNode
.
canRequestFocus
=
_isEnabled
;
}
...
...
@@ -963,10 +983,13 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
@override
void
didUpdateWidget
(
TextField
oldWidget
)
{
super
.
didUpdateWidget
(
oldWidget
);
if
(
widget
.
controller
==
null
&&
oldWidget
.
controller
!=
null
)
_controller
=
TextEditingController
.
fromValue
(
oldWidget
.
controller
.
value
);
else
if
(
widget
.
controller
!=
null
&&
oldWidget
.
controller
==
null
)
if
(
widget
.
controller
==
null
&&
oldWidget
.
controller
!=
null
)
{
_createLocalController
(
oldWidget
.
controller
.
value
);
}
else
if
(
widget
.
controller
!=
null
&&
oldWidget
.
controller
==
null
)
{
unregisterFromRestoration
(
_controller
);
_controller
.
dispose
();
_controller
=
null
;
}
_effectiveFocusNode
.
canRequestFocus
=
_canRequestFocus
;
if
(
_effectiveFocusNode
.
hasFocus
&&
widget
.
readOnly
!=
oldWidget
.
readOnly
&&
_isEnabled
)
{
if
(
_effectiveController
.
selection
.
isCollapsed
)
{
...
...
@@ -975,9 +998,35 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
}
}
@override
void
restoreState
(
RestorationBucket
oldBucket
,
bool
initialRestore
)
{
if
(
_controller
!=
null
)
{
_registerController
();
}
}
void
_registerController
()
{
assert
(
_controller
!=
null
);
registerForRestoration
(
_controller
,
'controller'
);
}
void
_createLocalController
([
TextEditingValue
value
])
{
assert
(
_controller
==
null
);
_controller
=
value
==
null
?
RestorableTextEditingController
()
:
RestorableTextEditingController
.
fromValue
(
value
);
if
(!
restorePending
)
{
_registerController
();
}
}
@override
String
get
restorationId
=>
widget
.
restorationId
;
@override
void
dispose
()
{
_focusNode
?.
dispose
();
_controller
?.
dispose
();
super
.
dispose
();
}
...
...
@@ -1122,60 +1171,64 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
}
Widget
child
=
RepaintBoundary
(
child:
EditableText
(
key:
editableTextKey
,
readOnly:
widget
.
readOnly
||
!
_isEnabled
,
toolbarOptions:
widget
.
toolbarOptions
,
showCursor:
widget
.
showCursor
,
showSelectionHandles:
_showSelectionHandles
,
controller:
controller
,
focusNode:
focusNode
,
keyboardType:
widget
.
keyboardType
,
textInputAction:
widget
.
textInputAction
,
textCapitalization:
widget
.
textCapitalization
,
style:
style
,
strutStyle:
widget
.
strutStyle
,
textAlign:
widget
.
textAlign
,
textDirection:
widget
.
textDirection
,
autofocus:
widget
.
autofocus
,
obscuringCharacter:
widget
.
obscuringCharacter
,
obscureText:
widget
.
obscureText
,
autocorrect:
widget
.
autocorrect
,
smartDashesType:
widget
.
smartDashesType
,
smartQuotesType:
widget
.
smartQuotesType
,
enableSuggestions:
widget
.
enableSuggestions
,
maxLines:
widget
.
maxLines
,
minLines:
widget
.
minLines
,
expands:
widget
.
expands
,
selectionColor:
selectionColor
,
selectionControls:
widget
.
selectionEnabled
?
textSelectionControls
:
null
,
onChanged:
widget
.
onChanged
,
onSelectionChanged:
_handleSelectionChanged
,
onEditingComplete:
widget
.
onEditingComplete
,
onSubmitted:
widget
.
onSubmitted
,
onAppPrivateCommand:
widget
.
onAppPrivateCommand
,
onSelectionHandleTapped:
_handleSelectionHandleTapped
,
inputFormatters:
formatters
,
rendererIgnoresPointer:
true
,
mouseCursor:
MouseCursor
.
defer
,
// TextField will handle the cursor
cursorWidth:
widget
.
cursorWidth
,
cursorHeight:
widget
.
cursorHeight
,
cursorRadius:
cursorRadius
,
cursorColor:
cursorColor
,
selectionHeightStyle:
widget
.
selectionHeightStyle
,
selectionWidthStyle:
widget
.
selectionWidthStyle
,
cursorOpacityAnimates:
cursorOpacityAnimates
,
cursorOffset:
cursorOffset
,
paintCursorAboveText:
paintCursorAboveText
,
backgroundCursorColor:
CupertinoColors
.
inactiveGray
,
scrollPadding:
widget
.
scrollPadding
,
keyboardAppearance:
keyboardAppearance
,
enableInteractiveSelection:
widget
.
enableInteractiveSelection
,
dragStartBehavior:
widget
.
dragStartBehavior
,
scrollController:
widget
.
scrollController
,
scrollPhysics:
widget
.
scrollPhysics
,
autofillHints:
widget
.
autofillHints
,
autocorrectionTextRectColor:
autocorrectionTextRectColor
,
child:
UnmanagedRestorationScope
(
bucket:
bucket
,
child:
EditableText
(
key:
editableTextKey
,
readOnly:
widget
.
readOnly
||
!
_isEnabled
,
toolbarOptions:
widget
.
toolbarOptions
,
showCursor:
widget
.
showCursor
,
showSelectionHandles:
_showSelectionHandles
,
controller:
controller
,
focusNode:
focusNode
,
keyboardType:
widget
.
keyboardType
,
textInputAction:
widget
.
textInputAction
,
textCapitalization:
widget
.
textCapitalization
,
style:
style
,
strutStyle:
widget
.
strutStyle
,
textAlign:
widget
.
textAlign
,
textDirection:
widget
.
textDirection
,
autofocus:
widget
.
autofocus
,
obscuringCharacter:
widget
.
obscuringCharacter
,
obscureText:
widget
.
obscureText
,
autocorrect:
widget
.
autocorrect
,
smartDashesType:
widget
.
smartDashesType
,
smartQuotesType:
widget
.
smartQuotesType
,
enableSuggestions:
widget
.
enableSuggestions
,
maxLines:
widget
.
maxLines
,
minLines:
widget
.
minLines
,
expands:
widget
.
expands
,
selectionColor:
selectionColor
,
selectionControls:
widget
.
selectionEnabled
?
textSelectionControls
:
null
,
onChanged:
widget
.
onChanged
,
onSelectionChanged:
_handleSelectionChanged
,
onEditingComplete:
widget
.
onEditingComplete
,
onSubmitted:
widget
.
onSubmitted
,
onAppPrivateCommand:
widget
.
onAppPrivateCommand
,
onSelectionHandleTapped:
_handleSelectionHandleTapped
,
inputFormatters:
formatters
,
rendererIgnoresPointer:
true
,
mouseCursor:
MouseCursor
.
defer
,
// TextField will handle the cursor
cursorWidth:
widget
.
cursorWidth
,
cursorHeight:
widget
.
cursorHeight
,
cursorRadius:
cursorRadius
,
cursorColor:
cursorColor
,
selectionHeightStyle:
widget
.
selectionHeightStyle
,
selectionWidthStyle:
widget
.
selectionWidthStyle
,
cursorOpacityAnimates:
cursorOpacityAnimates
,
cursorOffset:
cursorOffset
,
paintCursorAboveText:
paintCursorAboveText
,
backgroundCursorColor:
CupertinoColors
.
inactiveGray
,
scrollPadding:
widget
.
scrollPadding
,
keyboardAppearance:
keyboardAppearance
,
enableInteractiveSelection:
widget
.
enableInteractiveSelection
,
dragStartBehavior:
widget
.
dragStartBehavior
,
scrollController:
widget
.
scrollController
,
scrollPhysics:
widget
.
scrollPhysics
,
autofillHints:
widget
.
autofillHints
,
autocorrectionTextRectColor:
autocorrectionTextRectColor
,
restorationId:
'editable'
,
),
),
);
...
...
packages/flutter/lib/src/widgets/editable_text.dart
View file @
37ddad61
...
...
@@ -445,6 +445,7 @@ class EditableText extends StatefulWidget {
),
this
.
autofillHints
,
this
.
clipBehavior
=
Clip
.
hardEdge
,
this
.
restorationId
,
})
:
assert
(
controller
!=
null
),
assert
(
focusNode
!=
null
),
assert
(
obscuringCharacter
!=
null
&&
obscuringCharacter
.
length
==
1
),
...
...
@@ -1218,6 +1219,25 @@ class EditableText extends StatefulWidget {
/// Defaults to [Clip.hardEdge].
final
Clip
clipBehavior
;
/// Restoration ID to save and restore the scroll offset of the
/// [EditableText].
///
/// If a restoration id is provided, the [EditableText] will persist its
/// current scroll offset and restore it during state restoration.
///
/// The scroll offset is persisted in a [RestorationBucket] claimed from
/// the surrounding [RestorationScope] using the provided restoration ID.
///
/// Persisting and restoring the content of the [EditableText] is the
/// responsibilility of the owner of the [controller], who may use a
/// [RestorableTextEditingController] for that purpose.
///
/// See also:
///
/// * [RestorationManager], which explains how state restoration works in
/// Flutter.
final
String
restorationId
;
// Infer the keyboard type of an `EditableText` if it's not specified.
static
TextInputType
_inferKeyboardType
({
@required
Iterable
<
String
>
autofillHints
,
...
...
@@ -2323,6 +2343,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
controller:
_scrollController
,
physics:
widget
.
scrollPhysics
,
dragStartBehavior:
widget
.
dragStartBehavior
,
restorationId:
widget
.
restorationId
,
viewportBuilder:
(
BuildContext
context
,
ViewportOffset
offset
)
{
return
CompositedTransformTarget
(
link:
_toolbarLayerLink
,
...
...
packages/flutter/lib/src/widgets/restoration_properties.dart
View file @
37ddad61
...
...
@@ -4,6 +4,8 @@
// @dart = 2.8
import
'dart:async'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/services.dart'
;
...
...
@@ -257,11 +259,26 @@ class RestorableTextEditingController extends RestorableListenable<TextEditingCo
return
value
.
text
;
}
TextEditingController
_controller
;
@override
void
initWithValue
(
TextEditingController
value
)
{
_disposeControllerIfNecessary
();
_controller
=
value
;
super
.
initWithValue
(
value
);
}
@override
void
dispose
()
{
if
(
isRegistered
)
{
value
.
dispose
();
}
super
.
dispose
();
_disposeControllerIfNecessary
();
}
void
_disposeControllerIfNecessary
()
{
if
(
_controller
!=
null
)
{
// Scheduling a microtask for dispose to give other entities a chance
// to remove their listeners first.
scheduleMicrotask
(
_controller
.
dispose
);
}
}
}
packages/flutter/test/cupertino/text_field_restoration_test.dart
0 → 100644
View file @
37ddad61
// 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.
// @dart = 2.8
import
'package:flutter/cupertino.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/widgets.dart'
;
const
String
text
=
'Hello World! How are you? Life is good!'
;
const
String
alternativeText
=
'Everything is awesome!!'
;
void
main
(
)
{
testWidgets
(
'CupertinoTextField restoration'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
RootRestorationScope
(
child:
TestWidget
(),
restorationId:
'root'
,
),
);
await
restoreAndVerify
(
tester
);
});
testWidgets
(
'CupertinoTextField restoration with external controller'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
RootRestorationScope
(
child:
TestWidget
(
useExternal:
true
,
),
restorationId:
'root'
,
),
);
await
restoreAndVerify
(
tester
);
});
}
Future
<
void
>
restoreAndVerify
(
WidgetTester
tester
)
async
{
expect
(
find
.
text
(
text
),
findsNothing
);
expect
(
tester
.
state
<
ScrollableState
>(
find
.
byType
(
Scrollable
)).
position
.
pixels
,
0
);
await
tester
.
enterText
(
find
.
byType
(
CupertinoTextField
),
text
);
await
skipPastScrollingAnimation
(
tester
);
expect
(
tester
.
state
<
ScrollableState
>(
find
.
byType
(
Scrollable
)).
position
.
pixels
,
0
);
await
tester
.
drag
(
find
.
byType
(
Scrollable
),
const
Offset
(
0
,
-
80
));
await
skipPastScrollingAnimation
(
tester
);
expect
(
find
.
text
(
text
),
findsOneWidget
);
expect
(
tester
.
state
<
ScrollableState
>(
find
.
byType
(
Scrollable
)).
position
.
pixels
,
60
);
await
tester
.
restartAndRestore
();
expect
(
find
.
text
(
text
),
findsOneWidget
);
expect
(
tester
.
state
<
ScrollableState
>(
find
.
byType
(
Scrollable
)).
position
.
pixels
,
60
);
final
TestRestorationData
data
=
await
tester
.
getRestorationData
();
await
tester
.
enterText
(
find
.
byType
(
CupertinoTextField
),
alternativeText
);
await
skipPastScrollingAnimation
(
tester
);
await
tester
.
drag
(
find
.
byType
(
Scrollable
),
const
Offset
(
0
,
80
));
await
skipPastScrollingAnimation
(
tester
);
expect
(
find
.
text
(
text
),
findsNothing
);
expect
(
tester
.
state
<
ScrollableState
>(
find
.
byType
(
Scrollable
)).
position
.
pixels
,
isNot
(
60
));
await
tester
.
restoreFrom
(
data
);
expect
(
find
.
text
(
text
),
findsOneWidget
);
expect
(
tester
.
state
<
ScrollableState
>(
find
.
byType
(
Scrollable
)).
position
.
pixels
,
60
);
}
class
TestWidget
extends
StatefulWidget
{
const
TestWidget
({
Key
key
,
this
.
useExternal
=
false
})
:
super
(
key:
key
);
final
bool
useExternal
;
@override
TestWidgetState
createState
()
=>
TestWidgetState
();
}
class
TestWidgetState
extends
State
<
TestWidget
>
with
RestorationMixin
{
final
RestorableTextEditingController
controller
=
RestorableTextEditingController
();
@override
String
get
restorationId
=>
'widget'
;
@override
void
restoreState
(
RestorationBucket
oldBucket
,
bool
initialRestore
)
{
registerForRestoration
(
controller
,
'controller'
);
}
@override
void
dispose
()
{
controller
.
dispose
();
super
.
dispose
();
}
@override
Widget
build
(
BuildContext
context
)
{
return
MaterialApp
(
home:
Material
(
child:
Align
(
alignment:
Alignment
.
center
,
child:
SizedBox
(
width:
50
,
child:
CupertinoTextField
(
restorationId:
'text'
,
maxLines:
3
,
controller:
widget
.
useExternal
?
controller
.
value
:
null
,
),
),
),
),
);
}
}
Future
<
void
>
skipPastScrollingAnimation
(
WidgetTester
tester
)
async
{
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
200
));
}
packages/flutter/test/material/text_field_restoration_test.dart
0 → 100644
View file @
37ddad61
// 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.
// @dart = 2.8
import
'package:flutter/material.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/widgets.dart'
;
const
String
text
=
'Hello World! How are you? Life is good!'
;
const
String
alternativeText
=
'Everything is awesome!!'
;
void
main
(
)
{
testWidgets
(
'TextField restoration'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
RootRestorationScope
(
child:
TestWidget
(),
restorationId:
'root'
,
),
);
await
restoreAndVerify
(
tester
);
});
testWidgets
(
'TextField restoration with external controller'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
RootRestorationScope
(
child:
TestWidget
(
useExternal:
true
,
),
restorationId:
'root'
,
),
);
await
restoreAndVerify
(
tester
);
});
}
Future
<
void
>
restoreAndVerify
(
WidgetTester
tester
)
async
{
expect
(
find
.
text
(
text
),
findsNothing
);
expect
(
tester
.
state
<
ScrollableState
>(
find
.
byType
(
Scrollable
)).
position
.
pixels
,
0
);
await
tester
.
enterText
(
find
.
byType
(
TextField
),
text
);
await
skipPastScrollingAnimation
(
tester
);
expect
(
tester
.
state
<
ScrollableState
>(
find
.
byType
(
Scrollable
)).
position
.
pixels
,
0
);
await
tester
.
drag
(
find
.
byType
(
Scrollable
),
const
Offset
(
0
,
-
80
));
await
skipPastScrollingAnimation
(
tester
);
expect
(
find
.
text
(
text
),
findsOneWidget
);
expect
(
tester
.
state
<
ScrollableState
>(
find
.
byType
(
Scrollable
)).
position
.
pixels
,
60
);
await
tester
.
restartAndRestore
();
expect
(
find
.
text
(
text
),
findsOneWidget
);
expect
(
tester
.
state
<
ScrollableState
>(
find
.
byType
(
Scrollable
)).
position
.
pixels
,
60
);
final
TestRestorationData
data
=
await
tester
.
getRestorationData
();
await
tester
.
enterText
(
find
.
byType
(
TextField
),
alternativeText
);
await
skipPastScrollingAnimation
(
tester
);
await
tester
.
drag
(
find
.
byType
(
Scrollable
),
const
Offset
(
0
,
80
));
await
skipPastScrollingAnimation
(
tester
);
expect
(
find
.
text
(
text
),
findsNothing
);
expect
(
tester
.
state
<
ScrollableState
>(
find
.
byType
(
Scrollable
)).
position
.
pixels
,
isNot
(
60
));
await
tester
.
restoreFrom
(
data
);
expect
(
find
.
text
(
text
),
findsOneWidget
);
expect
(
tester
.
state
<
ScrollableState
>(
find
.
byType
(
Scrollable
)).
position
.
pixels
,
60
);
}
class
TestWidget
extends
StatefulWidget
{
const
TestWidget
({
Key
key
,
this
.
useExternal
=
false
})
:
super
(
key:
key
);
final
bool
useExternal
;
@override
TestWidgetState
createState
()
=>
TestWidgetState
();
}
class
TestWidgetState
extends
State
<
TestWidget
>
with
RestorationMixin
{
final
RestorableTextEditingController
controller
=
RestorableTextEditingController
();
@override
String
get
restorationId
=>
'widget'
;
@override
void
restoreState
(
RestorationBucket
oldBucket
,
bool
initialRestore
)
{
registerForRestoration
(
controller
,
'controller'
);
}
@override
void
dispose
()
{
controller
.
dispose
();
super
.
dispose
();
}
@override
Widget
build
(
BuildContext
context
)
{
return
MaterialApp
(
home:
Material
(
child:
Align
(
alignment:
Alignment
.
center
,
child:
SizedBox
(
width:
50
,
child:
TextField
(
restorationId:
'text'
,
maxLines:
3
,
controller:
widget
.
useExternal
?
controller
.
value
:
null
,
),
),
),
),
);
}
}
Future
<
void
>
skipPastScrollingAnimation
(
WidgetTester
tester
)
async
{
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
200
));
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment