我以此为参考来扩展和折叠扩展磁贴------- Flutter - Collapsing ExpansionTile after choosing an item
我想要的是,如果我打开了一个扩展图块,而用户单击了另一个扩展图块,则应该关闭其他打开的扩展图块。
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
void main() {
runApp(new ExpansionTileSample());
}
class ExpansionTileSample extends StatefulWidget {
@override
ExpansionTileSampleState createState() => new ExpansionTileSampleState();
}
class ExpansionTileSampleState extends State<ExpansionTileSample> {
final GlobalKey<AppExpansionTileState> expansionTile = new GlobalKey();
String foos = 'One';
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: const Text('ExpansionTile'),
),
body: GestureDetector(
onTap: () {
setState(() {
// this.foos = 'One';
expansionTile.currentState.collapse();
});
},
child: Column(
children: <Widget>[
new AppExpansionTile(
key: expansionTile,
title: new Text('1-3'),
backgroundColor:
Theme.of(context).accentColor.withOpacity(0.025),
children: <Widget>[
new ListTile(
title: const Text('One'),
),
new ListTile(
title: const Text('Two'),
),
new ListTile(
title: const Text('Three'),
),
]),
new AppExpansionTile(
title: new Text('4-6'),
backgroundColor:
Theme.of(context).accentColor.withOpacity(0.025),
children: <Widget>[
new ListTile(
title: const Text('four'),
),
new ListTile(
title: const Text('five'),
),
new ListTile(
title: const Text('six'),
),
]),
new AppExpansionTile(
title: new Text('6-9'),
backgroundColor:
Theme.of(context).accentColor.withOpacity(0.025),
children: <Widget>[
new ListTile(
title: const Text('seven'),
),
new ListTile(
title: const Text('eight'),
),
new ListTile(
title: const Text('nine'),
),
]),
],
),
),
),
);
}
}
// --- Copied and slightly modified version of the ExpansionTile.
const Duration _kExpand = const Duration(milliseconds: 200);
class AppExpansionTile extends StatefulWidget {
const AppExpansionTile({
Key key,
this.leading,
@required this.title,
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 ValueChanged<bool> onExpansionChanged;
final List<Widget> children;
final Color backgroundColor;
final Widget trailing;
final bool initiallyExpanded;
@override
AppExpansionTileState createState() => new AppExpansionTileState();
}
class AppExpansionTileState extends State<AppExpansionTile>
with SingleTickerProviderStateMixin {
AnimationController _controller;
CurvedAnimation _easeOutAnimation;
CurvedAnimation _easeInAnimation;
ColorTween _borderColor;
ColorTween _headerColor;
ColorTween _iconColor;
ColorTween _backgroundColor;
Animation<double> _iconTurns;
bool _isExpanded = false;
@override
void initState() {
super.initState();
_controller = new AnimationController(duration: _kExpand, vsync: this);
_easeOutAnimation =
new CurvedAnimation(parent: _controller, curve: Curves.easeOut);
_easeInAnimation =
new CurvedAnimation(parent: _controller, curve: Curves.easeIn);
_borderColor = new ColorTween();
_headerColor = new ColorTween();
_iconColor = new ColorTween();
_iconTurns =
new Tween<double>(begin: 0.0, end: 0.5).animate(_easeInAnimation);
_backgroundColor = new ColorTween();
_isExpanded =
PageStorage.of(context)?.readState(context) ?? widget.initiallyExpanded;
if (_isExpanded) _controller.value = 1.0;
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void expand() {
_setExpanded(true);
}
void collapse() {
_setExpanded(false);
}
void toggle() {
_setExpanded(!_isExpanded);
}
void _setExpanded(bool isExpanded) {
if (_isExpanded != isExpanded) {
setState(() {
_isExpanded = isExpanded;
if (_isExpanded)
_controller.forward();
else
_controller.reverse().then<void>((Null value) {
setState(() {
// Rebuild without widget.children.
});
});
PageStorage.of(context)?.writeState(context, _isExpanded);
});
if (widget.onExpansionChanged != null) {
widget.onExpansionChanged(_isExpanded);
}
}
}
Widget _buildChildren(BuildContext context, Widget child) {
final Color borderSideColor =
_borderColor.evaluate(_easeOutAnimation) ?? Colors.transparent;
final Color titleColor = _headerColor.evaluate(_easeInAnimation);
return new Container(
decoration: new BoxDecoration(
color: _backgroundColor.evaluate(_easeOutAnimation) ??
Colors.transparent,
border: new Border(
top: new BorderSide(color: borderSideColor),
bottom: new BorderSide(color: borderSideColor),
)),
child: new Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconTheme.merge(
data:
new IconThemeData(color: _iconColor.evaluate(_easeInAnimation)),
child: new ListTile(
onTap: toggle,
leading: widget.leading,
title: new DefaultTextStyle(
style: Theme
.of(context)
.textTheme
.subhead
.copyWith(color: titleColor),
child: widget.title,
),
trailing: widget.trailing ??
new RotationTransition(
turns: _iconTurns,
child: const Icon(Icons.expand_more),
),
),
),
new ClipRect(
child: new Align(
heightFactor: _easeInAnimation.value,
child: child,
),
),
],
),
);
}
@override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
_borderColor.end = theme.dividerColor;
_headerColor
..begin = theme.textTheme.subhead.color
..end = theme.accentColor;
_iconColor
..begin = theme.unselectedWidgetColor
..end = theme.accentColor;
_backgroundColor.end = widget.backgroundColor;
final bool closed = !_isExpanded && _controller.isDismissed;
return new AnimatedBuilder(
animation: _controller.view,
builder: _buildChildren,
child: closed ? null : new Column(children: widget.children),
);
}
}
答案 0 :(得分:2)
您可以使用ExpandablePanelList来执行展开和折叠视图。 使用expandingCallback保持活动状态以执行展开或折叠。 这是工作示例
class TestExpandableView extends StatefulWidget {
@override
_TestExpandableViewState createState() => _TestExpandableViewState();
}
class _TestExpandableViewState extends State<TestExpandableView> {
int _activeMeterIndex;
@override
Widget build(BuildContext context) {
return Container(
child: new ListView.builder(
itemCount: 2,
itemBuilder: (BuildContext context, int i) {
return Card(
margin:
const EdgeInsets.fromLTRB(10.0, 15.0, 10.0, 0.0),
child: new ExpansionPanelList(
expansionCallback: (int index, bool status) {
setState(() {
_activeMeterIndex = _activeMeterIndex == i ? null : i;
});
},
children: [
new ExpansionPanel(
isExpanded: _activeMeterIndex == i,
headerBuilder: (BuildContext context,
bool isExpanded) =>
new Container(
padding:
const EdgeInsets.only(left: 15.0),
alignment: Alignment.centerLeft,
child: new Text(
'list-$i',
)),
body: new Container(child: new Text('content-$i'),),),
],
),
);
}),
);
}
}
答案 1 :(得分:0)
您不必像@Aravindh Kumar那样在expansionCallback
中保持活动状态,而是可以使用ExpansionPanelList.radio
来接受ExpansionPanelRadio
的列表作为其子元素。此小部件最多可以打开列表中的一个面板,并自动为您保持活动状态。
请参阅flutter documentation for example usage。
// Flutter code sample for ExpansionPanelList.radio
// Here is a simple example of how to implement ExpansionPanelList.radio.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: MyStatefulWidget(),
),
);
}
}
// stores ExpansionPanel state information
class Item {
Item({
this.id,
this.expandedValue,
this.headerValue,
});
int id;
String expandedValue;
String headerValue;
}
List<Item> generateItems(int numberOfItems) {
return List.generate(numberOfItems, (int index) {
return Item(
id: index,
headerValue: 'Panel $index',
expandedValue: 'This is item number $index',
);
});
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
@override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
List<Item> _data = generateItems(8);
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Container(
child: _buildPanel(),
),
);
}
Widget _buildPanel() {
return ExpansionPanelList.radio(
initialOpenPanelValue: 2,
children: _data.map<ExpansionPanelRadio>((Item item) {
return ExpansionPanelRadio(
value: item.id,
headerBuilder: (BuildContext context, bool isExpanded) {
return ListTile(
title: Text(item.headerValue),
);
},
body: ListTile(
title: Text(item.expandedValue),
subtitle: Text('To delete this panel, tap the trash can icon'),
trailing: Icon(Icons.delete),
onTap: () {
setState(() {
_data.removeWhere((currentItem) => item == currentItem);
});
}));
}).toList(),
);
}
}
答案 2 :(得分:-2)
我认为您需要分别跟踪每个扩展图块的扩展状态。像这样:
Map<String, bool> expansionState = Map();
在初始化时,您需要指定键,以及是否要使关联的扩展图块显示为打开或关闭。在我的代码中,我有一个用于扩展图块名称的列表。在页面加载时,我将执行以下操作:
categoryList.forEach((name) {
expansionState.putIfAbsent(name, () => true);
});
然后,构建器仅引用expandationState:
Widget _buildCategory(String name, List<OrderItem> children) {
return ExpansionTile(
key: PageStorageKey<String>(name),
initiallyExpanded: expansionState[name], // true,
title: RichText(
text: TextSpan(
text: name,
style: TextStyle(
color: Colors.blue[800], fontSize: 16, fontWeight: FontWeight.bold),
children: <TextSpan>[
TextSpan(
text: ' - ' + children.length.toString() + ' items',
style: TextStyle(
color: Colors.grey[800],
fontSize: 16,
fontWeight: FontWeight.normal)),
],
)),
onExpansionChanged: ((newState) {
print(name + ' is now ' + newState.toString());
expansionState[name] = newState;
}),
children: children.map<Widget>(_buildChild).toList(),
);
}
这时,您可以添加一个按钮,该按钮的onPressed()
方法可以在setState()
调用中以所需的任何方式来操作expandState映射。您可以全部打开,全部关闭,切换其当前状态,等等。