By the end of this article, you will not only understand the implementation and composition of TextField, but also learn a lot of “weird” things that were not commonly used before.

TextField is a complex control in Flutter, and there are many different controls nested in the whole TextField, which constitute the commonly used input field effect. The following figure shows the main components of TextField, which is also the main content of this article.

FocusTrapArea

FocusTrapArea is a relatively new control. FocusTrapArea itself is nothing special. It’s just a FocusNode inserted into the RenderObject Tree.

It is mainly for Web/Desktop platform, by increasing the FocusTrapArea, perform the Web/Desktop platform TextEditingController. The clear, TextField also retains the focus it gained earlier.

Flutter issues: #86154, #86041

Effect of normal Abnormal effect

MouseRegion

It is used to handle mouse related events. It is used to respond to mouse exclusive Pointer events, such as mouse entering/leaving control area, cursor display effect, etc.

IgnorePointer

It is used in the TextField to handle whether the current input field is available or not, such as when widget.enabled or widget.decoration?. Enabled is false. IgnorePointer blocks gesture events in the entire region so that TextField is unable to click on input.

TextSelectionGestureDetectorBuilder

Less about TextSelectionGestureDetectorBuilder everyone should contact, and use is its subclasses in the TextField _TextFieldSelectionGestureDetectorBuilder:

It handles click, swipe, and long press events within TextField for EditableText, such as clicking to pop up the keyboard, long press to pop up the select copy/paste box, and so on.

In the interior of the TextSelectionGestureDetectorBuilder mainly through editableTextKey this GlobalKey to get to the EditableTextState, To associate the various gesture events with the actions in the EditableText.

This control is TextSelectionGestureDetector internal use.

For example, in _TextFieldSelectionGestureDetectorBuilder can see onSingleTapUp process:

As shown in the code above:

  • 1, Fold up the Toolbar that has popped up (oneOverlay, copy/paste, etc.);
  • 2. Select response events according to different platforms;
  • 3. Perform pop-up keyboard operation;
  • 4. Callback click events;

So if you need to do something with the TextField before clicking on the keyboard, the onTap of the TextField is not appropriate because it’s already popped.

Finally _TextFieldSelectionGestureDetectorBuilder will call buildGestureDetector method to generate a monitor and handle the touch control, used in nested child.

InputDecorator

InputDecorator internal parameter parsing is not much to be said here. It has been described in detail in previous books and should be familiar to anyone who has used TextField. The implementation of the InputDecorator in TextField is used in conjunction with the AnimatedBuilder.

Since both FocusNode and TextEditingController are ChangeNotifier (Listenable) in the TextField, they can be used with the animation of the AnimatedBuilder.

That is, when FocusNode and TextEditingController are changed, InputDecorator is rebuilt to change the render, for example, changing the background color of the input field when the input field is typed or when the focus changes.

Be careful not to confuse InputDecorator with InputDecorator. InputDecorator is used to configure InputDecorator.

So you can see that the InputDecorator has a lot of parameters and configuration. Developers can use the InputDecorator to create a lot of UI effects for the input box, but if you happen to have certain positions, or gaps that don’t fit the weird needs of the product, congratulations. You have started the cultivation of Flutter advanced development.

Why is that?

In short, InputDecorator is implemented internally as a custom RenderBox with more than 600 lines of layout-related code. That is, according to InputDecoration icon, prefixIcon, suffix and other parameters, locate the layout, calculate the location direction, adjust the location according to the baseline, and so on.

In addition, animations in the InputDecorator are performed primarily through internal AnimatedOpacity and other functions.

So in the case of inputDecorators, if you are not happy with some location or boundary effect, you either have to refactor your own implementation or you may have to “bend the rules”.

RepaintBoundary

Why is there a RepaintBoundary inside a TextField? First of all, what is RepaintBoundary?

This part of knowledge has been described in detail in the Comprehensive Analysis of Flutter Picture Rendering. To put it simply and loosely, RepaintBoundary is mainly used to form a Layer and obtain an independent drawing area.

It is common for Navigator to jump to the page. The internal base implementation has a RepaintBoundary to ensure that each area is an independent drawing area.

In addition, when it comes to Navigator, we have to say that each page also has its own FocusScope, namely the commonly used focusScope.of (Context) for keyboard and focus processing.

There is a RepaintBoundary inside the TextField, because the TextField itself is a control that needs to be updated frequently, and content changes in the TextField rarely need to trigger redrawing of the parent layout. Therefore, the existence of RepaintBoundary allows TextField to achieve better performance of local drawing.

UnmanagedRestorationScope

Less UnmanagedRestorationScope people might use it itself is a InheritedWidget, mainly is to share a RestorationBucket, A RestorationBucket is primarily concerned with saving/restoring an implementation state.

For example, when an application is reclaimed in the background due to low memory, it can be used to recover the specified data when it is returned to the App. For example:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // Give your RootRestorationScope an id, defaults to null.
      restorationScopeId: 'root', home: HomePage(), ); }}class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

// Our state should be mixed-in with RestorationMixin
class _HomePageState extends State<HomePage> with RestorationMixin {

  // For each state, we need to use a restorable property
  final RestorableInt _index = RestorableInt(0);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(child: Text('Index is ${_index.value}')),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _index.value,
        onTap: (i) => setState(() => _index.value = i),
        items: <BottomNavigationBarItem>[
          BottomNavigationBarItem(
              icon: Icon(Icons.home),
              label: 'Home'
          ),
          BottomNavigationBarItem(
              icon: Icon(Icons.notifications),
              label: 'Notifications'
          ),
          BottomNavigationBarItem(
              icon: Icon(Icons.settings),
              label: 'Settings'),,),); }@override
  // The restoration bucket id for this page,
  // let's give it the name of our page!
  String get restorationId => 'home_page';

  @override
  void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
    // Register our property to be saved every time it changes,
    // and to be restored every time our app is killed by the OS!
    registerForRestoration(_index, 'nav_bar_index'); }}Copy the code

As shown in the code above:

  • First of all toMaterialAppconfigurationrestorationScopeId(This function can be enabled only when configured.)
  • useRestorableIntUsed for configuration and savingBottomNavigationBarindex
  • inStateMixed withRestorationMixinAnd in therestoreStateRecovery in methodindexThe state;

Among them the default MaterialApp internal use to RootRestorationScope, and internal is UnmanagedRestorationScope RootRestorationScope; You can see the effect of the above example by opening Don’t Keep Activities in the emulator developer Settings.

The above example is from Introduction to State Restoration in Flutter.

Going back to TextField, a RestorationMixin in _TextFieldState, Then use the RestorableTextEditingController for for restoring TextEditingController.

Because the content of the input box by default to save in the TextEditingController TextEditingValue, so here with RestorableTextEditingController.

Normally, there is a RootRestorationScope inside the MaterialApp by default, so we just need to set restorationScopeId to the MaterialApp, And TextFild through built-in UnmanagedRestorationScope related logic, finally achieved the preservation and restoration of text content.

EditableText

EditableText needless to say, TextField’s ontology, which internally implements sliding through Scrollable, also uses a corresponding restorationId for recovery and caching.

First of all, notice that you can slide, and you can see that for an EditableText, it’s actually a ViewPort, and it slides based on a ViewPort to ffSet.

For EditableText internal, it USES the CompositedTransformTarget to implement the Toolbar and the linkage of the input box, namely input control and long press the “paste/copy” the link between the pop-up box.

So here simply introduce CompositedTransformTarget, it usually be used to control the linkage between with CompositedTransformFollower effect.

As shown in the above, common built-in the Slider, slide in the pop-up partial implementation, is realized by the combination of CompositedTransformTarget and CompositedTransformFollower, It allows one control to follow another without calculating its position, and they are linked together primarily by LayerLink.

Going back to TextField, in fact, in addition to the copy/paste Toolbar, EditableText does a similar thing internally with respect to the contents of the Selection area, Just here is directly by LeaderLayer rather than through its packaging CompositedTransformTarget to achieve them.

For using CompositedTransformTarget interested can refer to: juejin. Cn/post / 694641…

Use CompositedTransformTarget or, of course, there will be a “big” performance overhead, large-scale frequent use is not recommended, because after all, it belongs to a pushLayer operation.

The other part of the EditableText that draws content, which is basically what you know as the TextPainter, is nothing special, so I won’t go into that for now.

Therefore, this article mainly introduces the composition of TextField and explains the functions of its internal components, so that developers can have a clearer understanding of the implementation of text input boxes commonly used in Flutter. When problems or requirements are encountered, problems can be quickly located and solved. For example:

  • Where the “Paste/copy” Toolbar pops up;
  • How the Toolbar is positioned and laid out;
  • Click on theTextFieldIs how to pop up the keyboard and handle gesture events;
  • TextFieldHow to do local rendering;
  • .

Finally, someone just asked me a simple question: How to create a single line to multiple lines of wechat chat input field on Flutter, as shown in the following code:

TextField(
  focusNode: _focusNode,
  maxLines: 7,
  minLines: 1,
  decoration:
      const InputDecoration(border: OutlineInputBorder()),
)

Copy the code