<p dir="auto">React team recently announced hooks: <a href="https://medium.com/@dan_abramov/making-sense-of-react-hooks-fdbde8803889" rel="nofollow">https://medium.com/@dan_abramov/making-sense-of-react-hooks-fdbde8803889</a>. Due to how similar Flutter is with React, it would probably be interesting to see if these fits to Flutter too.</p>
<h3 dir="auto">The definition:</h3>
<p dir="auto">Hooks are very similar to the <code class="notranslate">State</code> of <code class="notranslate">StatefulWidget</code> with one main difference: We can have as many hooks as we like on a widget.<br>
Hooks have access to all the life-cycles that a <code class="notranslate">State</code> have (or a modified version).</p>
<p dir="auto">Hooks can be used on any given widget. Contrarily to <code class="notranslate">State</code> which can be used only for one specific widget type.</p>
<p dir="auto">Hooks are different from super mixins because they cannot generate conflicts. Hooks are <em>entirely</em> independent and unrelated to the widget.<br>
This means that a Hook can be used to store values and publicly expose it without fear of conflicts. It also means that we can reuse the same Hook multiple times, contrarily to mixins.</p>
<h3 dir="auto">The principle:</h3>
<p dir="auto">Hooks are basically stored within the <code class="notranslate">Element</code> in an Array. They are accessible only from within the <code class="notranslate">build</code> method of a widget. And hooks should be accessed unconditionally, example:</p>
<p dir="auto">DO:</p>
<div class="highlight highlight-source-dart notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="Widget build(BuildContext context) {
  Hook.use(MyHook());
}"><pre class="notranslate"><span class="pl-c1">Widget</span> <span class="pl-en">build</span>(<span class="pl-c1">BuildContext</span> context) {
  <span class="pl-c1">Hook</span>.<span class="pl-en">use</span>(<span class="pl-c1">MyHook</span>());
}</pre></div>
<p dir="auto">DON'T:</p>
<div class="highlight highlight-source-dart notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="Widget build(BuildContext context) {
  if (condition) {
    Hook.use(MyHook());
  }
}"><pre class="notranslate"><span class="pl-c1">Widget</span> <span class="pl-en">build</span>(<span class="pl-c1">BuildContext</span> context) {
  <span class="pl-k">if</span> (condition) {
    <span class="pl-c1">Hook</span>.<span class="pl-en">use</span>(<span class="pl-c1">MyHook</span>());
  }
}</pre></div>
<p dir="auto">This restriction may seem very limiting, but it is because hooks are stored by their index. Not their type nor name.<br>
This allows reusing the same hook as many time as desired, without any collision.</p>
<h3 dir="auto">The use case</h3>
<p dir="auto">The most useful part of Hooks is that they allow extracting life-cycle logic into a reusable component.</p>
<p dir="auto">One typical issue with Flutter widgets is disposable objects such as <code class="notranslate">AnimationController</code>.<br>
They usually require both an <code class="notranslate">initState</code> and a <code class="notranslate">dispose</code> override. But at the same time cannot be extracted into a mixin for maintainability reasons.</p>
<p dir="auto">This leads to a common code-snipper: <code class="notranslate">stanim</code></p>
<div class="highlight highlight-source-dart notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="class Example extends StatefulWidget {
  @override
  ExampleState createState() =&gt; ExampleState();
}

class ExampleState extends State&lt;Example&gt;
    with SingleTickerProviderStateMixin {
  AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: const Duration(seconds: 1));
  }

  @override
  void dispose() {
    super.dispose();
    _controller.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      
    );
  }
}"><pre class="notranslate"><span class="pl-k">class</span> <span class="pl-c1">Example</span> <span class="pl-k">extends</span> <span class="pl-c1">StatefulWidget</span> {
  <span class="pl-k">@override</span>
  <span class="pl-c1">ExampleState</span> <span class="pl-en">createState</span>() <span class="pl-k">=&gt;</span> <span class="pl-c1">ExampleState</span>();
}

<span class="pl-k">class</span> <span class="pl-c1">ExampleState</span> <span class="pl-k">extends</span> <span class="pl-c1">State</span>&lt;<span class="pl-c1">Example</span>&gt;
    <span class="pl-k">with</span> <span class="pl-c1">SingleTickerProviderStateMixin</span> {
  <span class="pl-c1">AnimationController</span> _controller;

  <span class="pl-k">@override</span>
  <span class="pl-k">void</span> <span class="pl-en">initState</span>() {
    <span class="pl-c1">super</span>.<span class="pl-en">initState</span>();
    _controller <span class="pl-k">=</span> <span class="pl-c1">AnimationController</span>(vsync<span class="pl-k">:</span> <span class="pl-c1">this</span>, duration<span class="pl-k">:</span> <span class="pl-k">const</span> <span class="pl-c1">Duration</span>(seconds<span class="pl-k">:</span> <span class="pl-c1">1</span>));
  }

  <span class="pl-k">@override</span>
  <span class="pl-k">void</span> <span class="pl-en">dispose</span>() {
    <span class="pl-c1">super</span>.<span class="pl-en">dispose</span>();
    _controller.<span class="pl-en">dispose</span>();
  }

  <span class="pl-k">@override</span>
  <span class="pl-c1">Widget</span> <span class="pl-en">build</span>(<span class="pl-c1">BuildContext</span> context) {
    <span class="pl-k">return</span> <span class="pl-c1">Container</span>(
      
    );
  }
}</pre></div>
<hr>
<p dir="auto">Hooks solves this issue by extracting the life-cycle logic. This leads to a potentially <em>much</em> smaller code:</p>
<div class="highlight highlight-source-dart notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="class Example extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    AnimationController controller = useAnimationController(duration: const Duration(seconds: 1));
    return Container(
      
    );
  }
}"><pre class="notranslate"><span class="pl-k">class</span> <span class="pl-c1">Example</span> <span class="pl-k">extends</span> <span class="pl-c1">StatelessWidget</span> {
  <span class="pl-k">@override</span>
  <span class="pl-c1">Widget</span> <span class="pl-en">build</span>(<span class="pl-c1">BuildContext</span> context) {
    <span class="pl-c1">AnimationController</span> controller <span class="pl-k">=</span> <span class="pl-en">useAnimationController</span>(duration<span class="pl-k">:</span> <span class="pl-k">const</span> <span class="pl-c1">Duration</span>(seconds<span class="pl-k">:</span> <span class="pl-c1">1</span>));
    <span class="pl-k">return</span> <span class="pl-c1">Container</span>(
      
    );
  }
}</pre></div>
<p dir="auto">A naive implementation of such hook could be the following function:</p>
<div class="highlight highlight-source-dart notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="AnimationController useAnimationController({Duration duration}) {
  // use another hook to obtain a TickerProvider
  final tickerProvider = useTickerProvider();

  // create an AnimationController once
  final animationController = useMemoized&lt;AnimationController&gt;(
    () =&gt; AnimationController(vsync: tickerProvider, duration: duration)
  );
  // register `dispose` method to be closed on widget removal
  useEffect(() =&gt; animationController.dispose, [animationController]), 

   // synchronize the arguments
  useValueChanged(duration, (_, __) {
    animationController.duration = duration;
  });

  return animationController;
}"><pre class="notranslate"><span class="pl-c1">AnimationController</span> <span class="pl-en">useAnimationController</span>({<span class="pl-c1">Duration</span> duration}) {
  <span class="pl-c">// use another hook to obtain a TickerProvider</span>
  <span class="pl-k">final</span> tickerProvider <span class="pl-k">=</span> <span class="pl-en">useTickerProvider</span>();

  <span class="pl-c">// create an AnimationController once</span>
  <span class="pl-k">final</span> animationController <span class="pl-k">=</span> <span class="pl-en">useMemoized</span>&lt;<span class="pl-c1">AnimationController</span>&gt;(
    () <span class="pl-k">=&gt;</span> <span class="pl-c1">AnimationController</span>(vsync<span class="pl-k">:</span> tickerProvider, duration<span class="pl-k">:</span> duration)
  );
  <span class="pl-c">// register `dispose` method to be closed on widget removal</span>
  <span class="pl-en">useEffect</span>(() <span class="pl-k">=&gt;</span> animationController.dispose, [animationController]), 

   <span class="pl-c">// synchronize the arguments</span>
  <span class="pl-en">useValueChanged</span>(duration, (_, __) {
    animationController.duration <span class="pl-k">=</span> duration;
  });

  <span class="pl-k">return</span> animationController;
}</pre></div>
<p dir="auto"><code class="notranslate">useAnimationController</code> is one of those "Hook".</p>
<p dir="auto">Where a naive implementation of such hook would be the following:</p>
<p dir="auto">That code should look similar to somebody used to <code class="notranslate">State</code> class. But this has a few interesting points:</p>
<ul dir="auto">
<li>The hook entirely takes care of the <code class="notranslate">AnimationController</code>, from creation to disposal. But also updates.</li>
<li>It can easily be extracted into a reusable package</li>
<li>It also takes care of <code class="notranslate">duration</code> updating on hot-reload as opposed to creating an <code class="notranslate">AnimationController</code> in the <code class="notranslate">initState</code> .</li>
<li>Hooks can use other hooks to build more complex logic</li>
</ul>
<h3 dir="auto">Drawbacks</h3>
<p dir="auto">Hooks comes with a cost. The access to a value has an overhead similar to an <code class="notranslate">InheritedElement</code>. And they also require to create a new set of short-lived objects such as closures or "Widget" like.</p>
<p dir="auto">I have yet to run a benchmark to see the real difference though</p>
<hr>
<p dir="auto">Another issue is about hot-reload.</p>
<p dir="auto">Adding hooks at the end of the list if fine. But since hooks work based on their order, adding hooks in the middle of existing hooks will cause a partial state reset. Example:</p>
<p dir="auto">Going from <code class="notranslate">A, B</code> to <code class="notranslate">A, C, B</code> will reset the state of <code class="notranslate">B</code> (calling both <code class="notranslate">dispose</code> and <code class="notranslate">initHook</code> again).</p>
<h3 dir="auto">Conclusion</h3>
<p dir="auto">Hooks simplifies <em>a lot</em> the widgets world by allowing a bigger code reuse. Especially on the very common scenarios such as dispose, memoize and watching a value.</p>
<p dir="auto"><strong>They can be used to entirely replace a StatefulWidget.</strong></p>
<p dir="auto">But they require a mind shift though, and the partial state reset on refactoring may be bothersome.</p>
<p dir="auto">It is possible to extract hooks outside of Flutter by creating custom Elements. There's no need to modify the sources as of now.</p>
<p dir="auto">But due to the specificity of hooks, it would benefit <em>a lot</em> from a custom linter. Which external packages cannot provide at the moment.</p>
<h4 dir="auto">Bonus</h4>
<p dir="auto"><del>A work in progress implementation is available here: <a href="https://github.com/rrousselGit/flutter_hooks">https://github.com/rrousselGit/flutter_hooks</a> (latest features are on <code class="notranslate">prod-ready</code> branch).</del></p>
<p dir="auto"><del>A release is planned soon as alpha. But the current implementation works to some extents.</del></p>
<p dir="auto">Available here <a href="https://pub.dartlang.org/packages/flutter_hooks#-readme-tab-" rel="nofollow">https://pub.dartlang.org/packages/flutter_hooks#-readme-tab-</a></p>
<p dir="auto">1 : Two TextField . For  Login Page<br>
2. First TextField KeyBoard IS SimpleChinese ,not click done then I focus PassWordField ,Crash</p>
<p dir="auto">Doctor summary (to see all details, run flutter doctor -v):</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="[✓] Flutter (Channel unknown, v0.10.1, on Mac OS X 10.14 18A391, locale zh-Hans-CN)
[!] Android toolchain - develop for Android devices (Android SDK 28.0.1)
    ! Some Android licenses not accepted.  To resolve this, run: flutter doctor
      --android-licenses
[!] iOS toolchain - develop for iOS devices (Xcode 10.0)
    ! CocoaPods out of date (1.5.0 is recommended).
        CocoaPods is used to retrieve the iOS platform side's plugin code that responds to
        your plugin usage on the Dart side.
        Without resolving iOS dependencies with CocoaPods, plugins will not work on iOS.
        For more info, see https://flutter.io/platform-plugins
      To upgrade:
        brew upgrade 

&gt; cocoapods

        pod setup
[✓] Android Studio (version 3.1)
[✓] Connected device (3 available)

! Doctor found issues in 2 categories."><pre class="notranslate"><code class="notranslate">[✓] Flutter (Channel unknown, v0.10.1, on Mac OS X 10.14 18A391, locale zh-Hans-CN)
[!] Android toolchain - develop for Android devices (Android SDK 28.0.1)
    ! Some Android licenses not accepted.  To resolve this, run: flutter doctor
      --android-licenses
[!] iOS toolchain - develop for iOS devices (Xcode 10.0)
    ! CocoaPods out of date (1.5.0 is recommended).
        CocoaPods is used to retrieve the iOS platform side's plugin code that responds to
        your plugin usage on the Dart side.
        Without resolving iOS dependencies with CocoaPods, plugins will not work on iOS.
        For more info, see https://flutter.io/platform-plugins
      To upgrade:
        brew upgrade 

&gt; cocoapods

        pod setup
[✓] Android Studio (version 3.1)
[✓] Connected device (3 available)

! Doctor found issues in 2 categories.
</code></pre></div>