Zero, preface,

FlutterUnit is an open source App for Flutter compilation and guidance that has been maintained for a long time. If you haven’t eaten Flutter yet, please refer to the Total Compilation: FlutterUnit Guide to Eating Flutter Open Source

Welcome to Star. Open source address: click me

FlutterUnit. Apk download Download FlutterUnit for MAC Github repository address

The solution to the Flutter Unit chapter will examine some of the implementation points of the project.

A lot of friends ask me, how do you do code folding panel? “ExpansionTile”? Do ExpansionTile will up and down the cable, very ugly, so I did not use ExpansionTile folding effect of core code in the source code: the components/project/widget_node_panel dart

. . .

I. Implementation scheme of AnimatedCrossFade

The core component is AnimatedCrossFade, which may not be used by many people, but is a very powerful component

You can search through the FlutterUnit app.

. . . .

1. Basic usage of AnimatedCrossFade
  • AnimatedCrossFadeIt can contain two componentsfirstChildandsecondChild
  • Enumeration state quantities can be specifiedcrossFadeState, has two valuesshowFirstandshowSecond
  • When the state quantity changes, either the first or the second is displayed depending on the state. Fades in and out when switching.
  • You can specify the animation duration. The following are200ms,400ms,600msThe effect of:
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: <Widget>[
          Row(
            children: <Widget>[
              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,
      );
}
Copy the code

And 2.ToggleRotatecombination

ToggleRotate is a very small component package I wrote, toGGle_rotate: ^0.0.5

For automatic rotation and rotation of components when clicked. See article: Toggle_rotate

45 degrees 90 degrees

The Flutter Unit is basically code panel folding based on this approach.


Second, the magic changeExpansionTileImplementation scheme

ExpansionTile’s source code was broadcast live on “B” last Saturday at 8:30 p.m.

As long as you understand the source code, in fact, magic change is so easy. You can also modify the _kExpand constant in the border to control the animation length.

Note: all magic changes to the source code, need to copy out, new file, do not directly change the source code.

The following code is processed and can be used directly.

Before going to edge After go to edge

// 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 <Widget>[],
    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<Widget> 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<Color> _borderColor;
  Animation<Color> _headerColor;
  Animation<Color> _iconColor;
  Animation<Color> _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: <Widget>[
          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() {
    finalThemeData 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 boolclosed = ! _isExpanded && _controller.isDismissed;return AnimatedBuilder(
      animation: _controller.view,
      builder: _buildChildren,
      child: closed ? null: Column(children: widget.children), ); }}Copy the code

On the livestream, ExpansionTile’s core implementation will be through ClipRect and Align

That’s right, the magical Align again, whose heightFactor controls the score of heights.

ClipRect(
  child: Align(
    alignment: Alignment.topCenter,
    heightFactor: _heightFactor.value,
    child: child,
  ),
),
Copy the code
The default is to start in the middle Alignment: alignment. TopCenter

So you can control where it comes from. Or that sentence: source code in hand, the world I have. Nothing to see the implementation of the source code, is very helpful to yourself. This is also the original intention between live source code, do not ask what learning method, learn debug, and then force yourself to see the source code is the fastest way to grow.

The cable wireless

The end of the

Welcome to Star and pay attention to the development of FlutterUnit. Let’s join hands and become a member of Unit.

In addition, I have a Flutter wechat communication group. You are welcome to join and discuss Flutter issues together. I look forward to exchanging ideas with you.

@ZhangFengjietele 2020.04.21 not allowed transfer

My official number: King of programming contact me – email :[email protected] – wechat :zdl1994328 ~ END ~