【Flutter Unit解牛編】コード折り畳み展開パネル、どうしてケーブルがないのですか?

13450 ワード

ゼロ、はじめに

FlutterUnit 【 】 Flutter 、 App , :【FlutterUnit食用ガイドライン】開源編
スターへようこそ.オープンソースアドレス:点我
FlutterUnit.apkダウンロード
FlutterUnit mac版ダウンロード
Github倉庫住所Flutter Unit は、プロジェクトのいくつかの実装ポイントを分析する.多くの友达は私に闻いて、 ?ExpansionTile ?确かにExpansionTileは上下に线があって、とてもみっともないで、 ExpansionTile は効果の核心コードを折り畳んでソースコードの:components/project/widget_node_panel.dart.
.
.

一、AnimatedCrossFade実現方案


コアのコンポーネントは:AnimatedCrossFadeで、あまり使われていないかもしれませんが、非常に強力なコンポーネントであり、FlutterUnit appで検索体験することができます.
.
.
.
.
1.AnimatedCrossFadeの基本的な使い方
  • AnimatedCrossFadeは、2つの構成要素firstChildおよびsecondChild
  • を含むことができる.
  • は、列挙状態量crossFadeStateを指定することができ、2つの値showFirstおよびshowSecond
  • がある.
  • 状態量が変化すると、状態に応じて1番目または2番目が表示される.切り替え時にフェードアウトします.
  • では、アニメーションの時間を指定できます.以下に200ms,400ms,600msの効果を示す:
  • 200ms
    400ms
    600ms
    class TolyExpandTile extends StatefulWidget {
    
      @override
      _TolyExpandTileState createState() => _TolyExpandTileState();
    }
    
    class _TolyExpandTileState extends State<TolyExpandTile>
        
        with SingleTickerProviderStateMixin {
      var _crossFadeState = CrossFadeState.showFirst;
    
      bool get isFirst => _crossFadeState == CrossFadeState.showFirst;
    
      @override
      Widget build(BuildContext context) {
        return Container(
          padding: EdgeInsets.all(20),
          child: Column(
            children: [
              Row(
                children: [
                  Expanded(
                    child: Container(),
                  ),
                  GestureDetector(
                      onTap: _togglePanel,
                      child: Padding(
                        padding: const EdgeInsets.all(8.0),
                        child: Icon(Icons.code),
                      ))
                ],
              ),
              _buildPanel()
            ],
          ),
        );
      }
    
      void _togglePanel() {
        setState(() {
          _crossFadeState =
              !isFirst ? CrossFadeState.showFirst : CrossFadeState.showSecond;
        });
      }
    
      Widget _buildPanel() => AnimatedCrossFade(
            firstCurve: Curves.easeInCirc,
            secondCurve: Curves.easeInToLinear,
            firstChild: Container(),
            secondChild: Container(
              height: 150,
              color: Colors.blue,
            ),
            duration: Duration(milliseconds: 300),
            crossFadeState: _crossFadeState,
          );
    }
    

    2.ToggleRotateとの併用

    ToggleRotateは私が書いた非常に小さなコンポーネントパッケージで、toggle_rotate: ^0.0.5はクリック時の の切り替えに使用されます.詳しくは記事を参照:toggle_rotate
    45度
    90度Flutter Unitは、基本的には、この方法に従って実装されるコードパネルの折り畳みである.
    -
    -

    二、魔改ExpansionTile実現方案

    8:30は、B でExpansionTileソースコードの解析を行った.ソースさえ読めば、実は魔が直してもso easyです.コアは、次のborderの鍋に注釈を付けるだけで、_kExpandの定数を修正してアニメーションの時間を制御することもできます. : , , , 。の次のコードは、処理後の である.
    縁取り線の前
    縁取り線を引いた後
    // Copyright 2017 The Chromium Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    
    import 'package:flutter/material.dart';
    import 'package:flutter/widgets.dart';
    
    const Duration _kExpand = Duration(milliseconds: 200);
    
    class NoBorderExpansionTile extends StatefulWidget {
    
      const ExpansionTile({
        Key key,
        this.leading,
        @required this.title,
        this.subtitle,
        this.backgroundColor,
        this.onExpansionChanged,
        this.children = const [],
        this.trailing,
        this.initiallyExpanded = false,
      }) : assert(initiallyExpanded != null),
            super(key: key);
    
      final Widget leading;
      
      final Widget title;
    
      final Widget subtitle;
      
      final ValueChanged<bool> onExpansionChanged;
      
      final List children;
    
      final Color backgroundColor;
    
      final Widget trailing;
    
      final bool initiallyExpanded;
    
      @override
      _ExpansionTileState createState() => _ExpansionTileState();
    }
    
    class _ExpansionTileState extends State<ExpansionTile> with SingleTickerProviderStateMixin {
      static final Animatable<double> _easeOutTween = CurveTween(curve: Curves.easeOut);
      static final Animatable<double> _easeInTween = CurveTween(curve: Curves.easeIn);
      static final Animatable<double> _halfTween = Tween<double>(begin: 0.0, end: 0.5);
    
      final ColorTween _borderColorTween = ColorTween();
      final ColorTween _headerColorTween = ColorTween();
      final ColorTween _iconColorTween = ColorTween();
      final ColorTween _backgroundColorTween = ColorTween();
    
      AnimationController _controller;
      Animation<double> _iconTurns;
      Animation<double> _heightFactor;
      Animation _borderColor;
      Animation _headerColor;
      Animation _iconColor;
      Animation _backgroundColor;
    
      bool _isExpanded = false;
    
      @override
      void initState() {
        super.initState();
        _controller = AnimationController(duration: _kExpand, vsync: this);
        _heightFactor = _controller.drive(_easeInTween);
        _iconTurns = _controller.drive(_halfTween.chain(_easeInTween));
        _borderColor = _controller.drive(_borderColorTween.chain(_easeOutTween));
        _headerColor = _controller.drive(_headerColorTween.chain(_easeInTween));
        _iconColor = _controller.drive(_iconColorTween.chain(_easeInTween));
        _backgroundColor = _controller.drive(_backgroundColorTween.chain(_easeOutTween));
    
        _isExpanded = PageStorage.of(context)?.readState(context) ?? widget.initiallyExpanded;
        if (_isExpanded)
          _controller.value = 1.0;
      }
    
      @override
      void dispose() {
        _controller.dispose();
        super.dispose();
      }
    
      void _handleTap() {
        setState(() {
          _isExpanded = !_isExpanded;
          if (_isExpanded) {
            _controller.forward();
          } else {
            _controller.reverse().then<void>((void value) {
              if (!mounted)
                return;
              setState(() {
                // Rebuild without widget.children.
              });
            });
          }
          PageStorage.of(context)?.writeState(context, _isExpanded);
        });
        if (widget.onExpansionChanged != null)
          widget.onExpansionChanged(_isExpanded);
      }
    
      Widget _buildChildren(BuildContext context, Widget child) {
    
        return Container(
          color: _backgroundColor.value ?? Colors.transparent,
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              ListTileTheme.merge(
                iconColor: _iconColor.value,
                textColor: _headerColor.value,
                child: ListTile(
                  onTap: _handleTap,
                  leading: widget.leading,
                  title: widget.title,
                  subtitle: widget.subtitle,
                  trailing: widget.trailing ?? RotationTransition(
                    turns: _iconTurns,
                    child: const Icon(Icons.expand_more),
                  ),
                ),
              ),
              ClipRect(
                child: Align(
                  heightFactor: _heightFactor.value,
                  child: child,
                ),
              ),
            ],
          ),
        );
      }
    
      @override
      void didChangeDependencies() {
        final ThemeData theme = Theme.of(context);
        _borderColorTween
          ..end = theme.dividerColor;
        _headerColorTween
          ..begin = theme.textTheme.subhead.color
          ..end = theme.accentColor;
        _iconColorTween
          ..begin = theme.unselectedWidgetColor
          ..end = theme.accentColor;
        _backgroundColorTween
          ..end = widget.backgroundColor;
        super.didChangeDependencies();
      }
    
      @override
      Widget build(BuildContext context) {
        final bool closed = !_isExpanded && _controller.isDismissed;
        return AnimatedBuilder(
          animation: _controller.view,
          builder: _buildChildren,
          child: closed ? null : Column(children: widget.children),
        );
      }
    }
    
    でExpansionTileのコア実装はClipRectAlignを通じて間違いなく、また不思議なAlignであり、そのheightFactorは高度な分率を制御することができる.
    ClipRect(
      child: Align(
        alignment: Alignment.topCenter,
        heightFactor: _heightFactor.value,
        child: child,
      ),
    ),
    

    デフォルトは中央から
    設定alignment:Alignment.topCenter
    これでどこから現れるかを制御できます.やはりあの言叶: , 。大丈夫多くソースコードの実现を见て、自分に対してとても役に立ちます.これも生放送 の初志です.もう勉強方法を聞かないでください. debug, 。有線
    ワイヤレス

    の最後の部分


    StarとFlutterUnitの発展に注目して、一緒に手を携えてUnitの一員になりましょう.その他に私は1つのFlutterの微信の交流グループがあって、小さい仲间を歓迎して参加して、共にFlutterの问题を探求して、あなたとの交流と切磋琢磨を期待します.@ 2020.04.21 : -- :[email protected] -- :zdl1994328 ~ END ~