问题
I'm trying to make an Expandable Column. That is, it has 2 children: the child and the expanded child that appears when the widget is expanded.
In the picture below, you see the child (blue) and expanded child (red) which should only appear when the Expand >
button is clicked.
Everything is working, but no mater what I do, I cannot get the Expand >
button to work like this:
Expand >
when not expanded, and Expand (lots of spaces) <
when expanded.
This is the code of the Expand button:
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
//Expanded() would be the solution here, but it breaks cause it's inside ListView
Container(
child: Container(
margin: const EdgeInsets.only(left: 12, top: 15),
child: Text(this.title,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w700,
fontFamily: 'Roboto',
color: Colors.white)),
)),
//Expanded() would be the solution here, but it breaks cause it's inside ListView
Container(
child: Container(
margin: const EdgeInsets.only(right: 7, top: 15),
child: expanded != null && expanded
? Icon(Icons.arrow_back_ios_outlined,
color: Colors.grey)
: Icon(Icons.arrow_forward_ios,
color: Colors.grey))),
])
The obvious solution would be to make this Row
have 2 Expanded
children instead of 2 Container
, but if you do that everything brakes because it's inside a ListView
scrollable horizontally, so it tries to expand forever.
Entire code in Dartpad:
https://dartpad.dev/dccfb8c0efa98f5da64eca625ccf1487
If you substitute Container()
by Expanded()
as I mentioned, the layout wont render.
I've been thinking for a long time on how to fix this, but the ExpandableListTitle
can't possibly know the size of its parent since the size of its parent depends on the size of the children: expanded and not expanded.
PS: The ListView
cannot be changed by a Row
, I really need to scroll between these expandable widgets horizontally.
回答1:
You were passing the function wrong. Please check the code now.
import 'package:flutter/material.dart';
final Color darkBlue = const Color.fromARGB(255, 18, 32, 47);
const double COLUMN_WIDTH = 150;
const double COLUMN_WIDTH_EXPANDED = COLUMN_WIDTH;
const double COLUMN_WIDTH_TOTAL_EXPANDED = COLUMN_WIDTH + COLUMN_WIDTH_EXPANDED;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: Container(
height: 300,
child: ListView(scrollDirection: Axis.horizontal, children: [
ExpandableColumn(
title: "Expand",
child: Container(width: COLUMN_WIDTH, color: Colors.blue),
expandedChild: Container(
width: COLUMN_WIDTH_EXPANDED, color: Colors.red),
expanded: false,
),
ExpandableColumn(
title: "Expand",
child: Container(width: COLUMN_WIDTH, color: Colors.green),
expandedChild: Container(
width: COLUMN_WIDTH_EXPANDED, color: Colors.purple),
expanded: false,
),
]))),
),
);
}
}
class ExpandableColumn extends StatefulWidget {
const ExpandableColumn(
{Key key,
this.title,
@required this.child,
this.expandedChild,
this.onExpand,
this.expanded})
: super(key: key);
final Widget child;
final Widget expandedChild;
final Function onExpand;
final String title;
final bool expanded;
@override
ExpandableColumnState createState() => ExpandableColumnState();
}
class ExpandableColumnState extends State<ExpandableColumn>
with TickerProviderStateMixin {
bool expIcon = false;
AnimationController _controller;
Animation<double> _animation;
//Animation<double> _animationT;
Function onExpand;
bool expanded;
final Duration duration = const Duration(milliseconds: 200);
@override
void dispose() {
super.dispose();
_controller.dispose();
}
@override
void initState() {
super.initState();
onExpand = widget.onExpand;
expanded = widget.expanded;
_controller = AnimationController(
duration: duration,
vsync: this,
);
_animation = CurvedAnimation(
parent: _controller,
curve: Curves.fastOutSlowIn,
);
}
updateExpansion() {
// if (expanded) {
// _controller.forward();
// } else {
// _controller.reverse();
// }
!expanded
? Future.delayed(duration).then((value) {
expIcon = !expIcon;
_controller.reverse();
setState(() {});
})
: _controller
.forward()
.then((value) => setState(() => expIcon = !expIcon));
}
onPressed() {
expanded = !expanded;
updateExpansion();
setState(() {});
}
@override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.topLeft,
child: Container(
child: Column(
//mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ExpandableListTitle(
title: widget.title,
expanded: expanded,
onPress: onPressed,
expIcon: expIcon,
columnWidth: expanded
? (_animation.value * 150 + 150)
: expIcon
? (_animation.value * 150 + 150)
: COLUMN_WIDTH,
duration: duration,
// onPress: () => {
// setState(() {
// expanded != null ? expanded = !expanded : expanded = true;
// updateExpansion();
// if (onExpand != null) onExpand(expanded);
// })
// },
),
Expanded(
child: Container(
child:
Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
Container(
width: COLUMN_WIDTH,
child: widget.child,
),
SizeTransition(
sizeFactor: _animation,
axis: Axis.horizontal,
axisAlignment: -1,
child: Container(
width: COLUMN_WIDTH, child: widget.expandedChild))
]),
))
])),
);
}
}
class ExpandableListTitle extends StatefulWidget {
const ExpandableListTitle({
Key key,
this.title,
this.expanded,
this.onPress,
this.expIcon,
this.columnWidth,
this.duration,
}) : super(key: key);
final String title;
final bool expanded;
final Function onPress;
final bool expIcon;
final double columnWidth;
final Duration duration;
@override
_ExpandableListTitleState createState() => _ExpandableListTitleState();
}
class _ExpandableListTitleState extends State<ExpandableListTitle>
with SingleTickerProviderStateMixin {
bool _alignAnimationEnded = false;
// bool expanded;
// Function onPress;
// String title;
// @override
// void initState() {
// super.initState();
// expanded = widget.expanded;
// onPress = widget.onPress;
// title = widget.title;
// }
@override
Widget build(BuildContext context) {
return Container(
width: widget.columnWidth,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
// onTap: () =>
// onPress != null ? onPress() : print('ExpandableListTitle tap'),
onTap: () {
_alignAnimationEnded = false;
widget.onPress();
},
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
child: Container(
margin: const EdgeInsets.only(left: 12, top: 15),
child: Text(this.widget.title,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w700,
fontFamily: 'Roboto',
color: Colors.white)),
)),
Expanded(
flex: 1,
child: Container(
margin: const EdgeInsets.only(right: 7, top: 15),
child: AnimatedAlign(
onEnd: () => setState(() => _alignAnimationEnded = true),
duration: widget.duration,
alignment: widget.expanded && widget.expIcon
? Alignment.centerRight
: Alignment.centerLeft,
child: widget.expanded
? widget.expIcon && _alignAnimationEnded
? const Icon(Icons.arrow_back_ios_outlined,
color: Colors.grey)
: const Icon(Icons.arrow_forward_ios,
color: Colors.grey)
: widget.expIcon
? const Icon(Icons.arrow_back_ios_outlined,
color: Colors.grey)
: const Icon(Icons.arrow_forward_ios,
color: Colors.grey),
),
),
),
],
),
),
);
}
}
来源:https://stackoverflow.com/questions/65296595/simple-expandable-column-on-flutter-wont-have-title-expandable-too