零、前言

FlutterUnit是【张风捷特烈】长期维护的一个Flutter集录、指南的开源App
假如你还未食用,可拜见总汇集: 【 FlutterUnit 食用指南】 开源篇

欢迎 Star。 开源地址: 点我

FlutterUnit.apk 下载 Github仓库地址
【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?
【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?

Flutter Unit 解牛篇 将对项目的一些完成点进行剖析。
许多朋友问我,你代码折叠面板怎样做的?ExpansionTile打开的线去不掉吧?
确实ExpansionTile打开上下会有线,十分丑陋,所以我未G - ; $ y运用ExpansionTile计划
折叠作用的{ 4 b中心代码在源码的:
components/project/widget_node_panel.dart

. . .X B R 3
【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?
【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?
【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?

一、AnimatedCrossFade完成计划

中心的组件是: AnimatedCrossFade,或许很少人用,但它是一个十分强壮的组件
你能够在FlutterUnit app中进行搜索体会。

. . . .
【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?
【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?
【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?
【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?

1. AnimatedCroE # c 3 _ + [ *ssFade的根本用法
  • AnimatedCrossFade能够包含两个组件firstChildsecondChild
  • 可指定枚举状况量crossFadeState,有两个值showFirstshowSecond
  • 当状况量改变时,I | K W V会依据状况显示第一个或第二个。切换时会淡入淡出。
  • 能够指定动画时长。如下分别是200ms,400ms,600ms的作用:
200ms 400ms 600ms
【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?
【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?
【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?
class TolyExpandTile extends StatefulWidget {
@override
_TolyE# d expandTileState createState() => _ToM 5 # v mlyExpandTileState();
}
class _TolyExpandTileState extends State<TolyExpandTile>
with SingleTickerProviderStateMixin {
var _crossFaC . s I d BdeState = CrossFadeState.shD y C M w yowFirst;
bool get isFirst => _crossFadeState == CrossFadeState.shu 3 #owFirst;
@override
Widg: / B A G get build(BuildContext context) {
return Container(
padding: EdgeInsets.all(20),
child: Column(
children: <Widget>[
Row(
chiz . W 6ldren: <Widget>[
Expanded(
child: CoT } C ( Qntainer(),
),
GestureDf , 0 2etector(
onTap: _togglePanel,
child: Padding(
paddint B F n E u R 1 Gg: const EdgeInsets.all(8.0)} L M %,
child: Icon(Icons.code),
))
],
),
_buildPanel()
],
)r V = 1 6 w,
);
}
void _togglePanel() {
setStat] { Z Z p 3 f O `e(() {
_crossFadeState =
!isFirst ? CrossFadeState.showFirst : CrossFadeState.showSecond;
});
}
Widget _buildPanel() => AnimatedCrossFad? } I v L L i a Ee(
firstCurve: Curves.easeInCirc: d { 5,
secondCurve: Curves.easeInToLinear,
firstChild: Container(),
secondChild: Con+ A G t itainer(
heis 4 Mght: 150,
colN r T - [ p 8 rort m 5 o s 7 x: Co! f Q w ~ 7 A elors.blue,
),
duration: Duratib & J Pon(milliseconds: 300),
crossFadeState: _crossFadeState,
);
}

2. 和ToggleRotate联用

ToggleRotate是我写的一个十分小的组件包, toggle_rotate: ^0.0.5
用于点击时组件主动旋转转回的切换。详见文章: toggle_rotate

45度 90度
【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?
【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?

Flutteg o k r . N 7 fr Unit根本便是依据这种办法完成的代码面板折叠。

【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?
【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?

二、魔改ExpansionTile完成计划

上星期六晚8:30B站直播了El % ! V b 4 sxpansionTile源码h p 9 e b的解析。
只需看懂源码,其实魔改一下也是so easy 的。中心便是下面border的锅
注释掉即可, 你也能够修正其间的_kExpand常量来控a d e C制动画的时长。

【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?

注意: 全部对源码的魔改,| T : z k 1 !都需要复制出来,新建文件,别直接改源码。
下面的代码是处理之后的,能够拿去直接用

去边线前 去边线后} 6 g 0 & ( y W R
【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?
【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?

// 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:f3 _ N ] - ! ~ ^lutter/w/ F t  ? 1 pidgets.dart';
const Duration _kExpand = Duration(mil@ ~ *liseconds: 200);
class NoBorderExpansionTile eO h - 2xtends StatefulWidget {
const ExpansionTile({
Key key,
this.leading,
@required this.title,
this.subtitle,
this.backgroundColor,
this.onExpansionChanged,
this.childk = S w y 4rj u S J K Q en = const <Widget>[],
this.trailing,
this.initiallyExpanded = false,
}) : assert(initiallyExpanded != null),
super(key: keyn / V f % ; q U);
fina@ b Ul Widget leading;
final Widget title;
final Wn { B Z U j 0idget subtitle;
final ValueChanged<bool> onExpansionChanged;
final List<Widget> children;
final Colo2 m Y 8 d V 2r backgroundColor;
fio j % wnal Widget trailing;
final bool ini^ s $ -ti0 ? $ Y 9 R i : (allyExpanded;
@override
_ExpansionTileSta8 g ; w 8te createState() => _ExpansionTileState();
}
class _Expah T D M r  3 u :nsionTileStatB I T @ 3 f 0 ( $e extends State<ExpansionTile> wiG 2 Y ! +th SingleTickerProvidB y P r L 6 v 0 zerStateMixin {
static final Animatable<double> _easeOutTween = CurveTween(cuB G g & )rve: Curves.easeOut);
static final Animatable<double&W  ; $ Q ! S s _gt; _easeInTween = CurveTween(curve: Curves.ee F F D aaseIn);
static final Animatable<double> _halfTween = Tween<double>(begin: 0.0, end: 0.5);
fz V S  w g Minal ColorTween _bordM T perColorT1 q Aween = ColorTween();
final ColorTween _hear B 6 :derColorTween = ColorT! E q 2 _ )  Sween();
final ColorTween _iconColorTween = ColorTween();
final ColorTween _backgrouX c b , WndColorTween = ColorTween();
AnimationController _controller;
Animatic G ton<double> _iconTurns;
AnimaC + vtion<double> _heightFactor;
Animatio| } xn<Color> _borderColor;
Animation<Color> _headerColor;
Animation<Color> _icoX + G YnColor;
Animation<Color> _backgroundC7 ( T ^ : v V Aolor;
bol T e o ` Zol _isExpW ~ Wanded = false;
@override
void ia g j . k ( , ! 3nitState() {
super.initState();
_controller =I ` / AnimationController(dura2 - C z )tion: _kExpand@ C R f O, vsync: this);
_heightFactor = _controlD ~ V Q } q @ler.drive(_easeInTween);
_iconTurns = _controller.drM x I Q r a }iv_ z xe(6 N q L _ u 9_halfTween.chain(_easeInTween));
_borderColor = _controller.drive(_borderColorTween.chain(_easu ~ # O ! oeOutTween));
_headerColor = _controller.drive(_headerC^ $ J ] w * 0olorTween.chain(_easeInTween));
_iconColor = _controller.drive(G K R_iconColou t g C XrTween.chain(_easeInTween));
_backgroundColor = _co_ A ]ntroller.driv( G P _ + { H L ze(_backgroundColorT| ? 8 S s Nw9 ) . T e ` H 2een.chain(_easeOutTween));
_is- ` ` k !Expanded = PageStorage.of(contx g p Q Y Q S [ext)?.readState_ z :(coD T ` C :ntext) ?? widget.initiallyExpanded;
if (_isExpanded)
_controller.value = 1.0;
}
@oveG & { - E P Jrride
void dispose() {
_controller.dispose();
super.dispose();
}
void _handlJ B n (eTap() {
setState(() {
_isExpanded = !_isExp/ H M ^ Fanded;
if (_i{ T ; 7 % qsExpanded) {
_controller.forward();
}x 5 z T o else {
_controller.reverse().then<void>((void value) {
if (!mounted)
return;
setState(() {
// Rebuild without widget.children.
});
});
}
PageStorage.of(context)?.writeStA ` , qate(conteM = ^ 6 bxt, _isExpanded);
});
if (widget.onExpansionChanged != null)
widget.onExpansionChanged(_isExpanded);
}
Widget _buildChildren(BuildContext context, Wid0 y B * lget chS ] rild) {
return Container(
color: _backgroundColor.valueT Y [ | i B ` ?? Colors.t7 ` Kransparent,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTileTheme.merge(
iconCI S @ l , 1 Xolor: _iconColor.vU | ~ ) ] O J 7 Qalue,
textColor: _headerColor.value,
chi1 g _ C ; 8 fld: ListK A e 8 A N KTile(
onTap: _handleTap,
leading: widget.leading,
title: widget.title,
s{ : { ? s k E ^ Rubtitle: widget.subtitle,
trailing: widget.trailing ?? RotationTransition(
tc 6 S b _ # G S &urns: _iconTurns,
child: const Icon(Icons.expand_more),
),
),
),
ClipRect([ 7 O _ } * o
child: Align(
heightFacX | I ~ P R L stor: _heightFactor.value,
cN o { G 2 /hild: child,
),
),
],
),
);
}
@override
void didChange I 9 8 q ` y U dDependencies() {
final TI Q _ H ohemeData theme = Theme.of(context);
_borderColorTween
..end = theme.dividerColor;
_headerColorTween
..begin =B V 5 theme.text9 X = ^Theme.subhead.color
..end = theme.accentColor;
_$ ^ l qiconCo W Y G Z z G 6lorTween
..begin = theme.unselectedWidgetColor
..end =# 9 e n , m = ( theme.accentCol. * ]or;
_backgroundColorTween
..end = widget.backgroundColor;
super.didChangeDependeA J % O X  z lncies();
}
@override
Widget build(BuildCT I M H P t O ^ontext cont; L - - # z 8 cext) {
final bool closed = !_isExpandeT # ; $ $ -d && _controller.isDismissed;
return Anim` E ^ ` hatedBuilder(
animation: _controller.view,
builder: _buildChildrd ! Z ~ | E H V 2en,
child: closed( W t ? null : Column(childre^ M g n: widget.children),
);
}
}

直播中说了ExpansionT1 Q N r 2ile的中心完成是通过ClipRectAlign
没错,又是神奇的Align,它的heightFactor能够控制高度的分率。

ClipReb 8 - | f ; b 4 8ct(
child: AlignL W ! ~ } ! } 9 W(
alignment:o S Y T 7 t 0 Alignment.topCenter,
height! X ^ o xFactt i | for: _heightFactor.value,
child: child,
),
),
默认从中心开端 设置alignment: Alignment.topCenter
【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?
【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?

E ; W H ;样就0 S o z 6 5 . 6 ]能控制从~ , ; 5 O G J哪里呈现。仍是那句话: 源码在手,天下我有。没事多看看源码的完成,对自己是很有帮助的。这也是直播源码之间的初衷,别再问什么学习办法了,学会debug,然后逼自己看源码是最快的生长方法。

有线 无线
【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?
【 Flutter Unit 解牛篇 】代码折叠展开面板,怎么没有线?

结尾

欢迎Star和重视Flutter$ + SUnit 的开展,让我们一起携手,成为UniO K vt一员。
别的本人有一个Flutter微信沟通群,欢迎小伙伴加入,共同探讨Flutter的问题,等待与你的沟通与切磋。

@张风捷特烈 202w W o W . w b - l0.04.21 未允禁转
v ^ f . l的公众号:编程之王
联系我--邮箱:1981462002@qq.com --微信:zdl1994328
~ END ~