I’m trying to make it so that two widgets are placed in a row that divide it in half. When you click on one of them, it moved apart, and the second disappeared. But so far I get overflow when closing.
import 'package:flutter/material.dart';
void main() {
runApp(const AccountSecurityPage());
}
class AccountSecurityPage extends StatefulWidget {
const AccountSecurityPage({super.key});
@override
State<AccountSecurityPage> createState() => _AccountSecurityPageState();
}
enum SelectedField { email, psw }
class _AccountSecurityPageState extends State<AccountSecurityPage> {
SelectedField? field;
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(title: const Text("Account Security")),
body: Column(
children: [
const Column(
children: [
Row(
children: [
Text(
"Two-factor authentication",
style: TextStyle(fontWeight: FontWeight.w700),
),
],
),
Wrap(
children: [
Text(
"To protect your account, we recommend enabling at least one type of two-factor authentication")
],
)
],
),
Row(
mainAxisSize: MainAxisSize.max,
children: [
//SliderAppScreen(),
HorizontalExpandedField<SelectedField>(
value: SelectedField.email,
groupValue: field,
onChanged: (SelectedField? val) {
if (field != SelectedField.email) {
setState(() {
field = SelectedField.email;
});
} else {
setState(() {
field = null;
});
}
},
),
HorizontalExpandedField<SelectedField>(
value: SelectedField.psw,
groupValue: field,
onChanged: (SelectedField? val) {
if (field != SelectedField.psw) {
setState(() {
field = SelectedField.psw;
});
} else {
setState(() {
field = null;
});
}
},
),
],
)
],
),
),
);
}
}
class HorizontalExpandedField<T> extends StatefulWidget {
final T value;
final T? groupValue;
final ValueChanged<T?> onChanged;
const HorizontalExpandedField({
super.key,
required this.value,
required this.groupValue,
required this.onChanged,
});
@override
State<HorizontalExpandedField<T>> createState() =>
_HorizontalExpandedFieldState();
}
class _HorizontalExpandedFieldState<T> extends State<HorizontalExpandedField<T>>
with TickerProviderStateMixin {
late final AnimationController _expandedController;
late final AnimationController _scaleContainerController;
late Animation _expandedAnimation;
late final Animation<double> _scaleContainerAnimation;
@override
initState() {
_scaleContainerController = AnimationController(
duration: const Duration(milliseconds: 200), vsync: this);
_scaleContainerAnimation = CurvedAnimation(
parent:
Tween<double>(begin: 0.0, end: 1).animate(_scaleContainerController),
curve: Curves.easeOut,
);
super.initState();
_expandedController = AnimationController(
duration: const Duration(milliseconds: 300), vsync: this);
_expandedAnimation =
IntTween(begin: 1, end: 20).animate(_expandedController);
_expandedAnimation.addListener(() => setState(() {}));
_scaleContainerController.addListener(() => setState(() {}));
}
@override
dispose() {
_scaleContainerController.dispose();
_expandedController.dispose();
super.dispose();
}
void _handleChanged(bool selected) {
if (!selected) {
_expandedController
.forward()
.then((value) => _scaleContainerController.forward());
widget.onChanged(widget.value);
} else {
if (!isCollapse) {
_scaleContainerController
.reverse()
.then((value) => _expandedController.reverse());
}
widget.onChanged(null);
}
return;
}
bool get _isSelected => widget.value == widget.groupValue;
bool get isCollapse => !_isSelected && widget.groupValue != null;
@override
Widget build(BuildContext context) {
return Expanded(
flex: _expandedAnimation.value,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: MaterialButton(
clipBehavior: Clip.hardEdge,
onPressed: () {
_handleChanged(_isSelected);
},
color: const Color(0xFFF8F8F8),
elevation: 0,
padding: const EdgeInsets.all(16),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: _isSelected
? CrossAxisAlignment.start
: CrossAxisAlignment.center,
children: [
Expanded(
child: Flex(
clipBehavior: Clip.hardEdge,
direction: _isSelected ? Axis.horizontal : Axis.vertical,
crossAxisAlignment: CrossAxisAlignment.center,
children: const <Widget>[
Icon(Icons.email_rounded),
Text(softWrap: false, "email"),
],
),
),
const Expanded(child: Icon(Icons.expand_circle_down)),
],
),
//if (_isSelected && !isCollapse)
ScaleTransition(
scale: _scaleContainerAnimation,
alignment: Alignment.centerLeft,
child: Container(
decoration: const BoxDecoration(
color: Colors.red,
),
clipBehavior: Clip.hardEdge,
height: _scaleContainerAnimation.value * 50,
width: double.infinity,
child: Column(
children: [
Expanded(
child: Row(
children: [
Text(softWrap: false, "email"),
Expanded(child: TextField()),
],
),
),
],
),
),
)
],
),
),
),
);
}
}
I was thinking of using Hero widgets, but I didn’t understand how to implement it without going through roots.
There is an option to use Builder and animate through it, but this is too cumbersome and inconvenient design
2
Answers
Thanks to @YasinShah answer , I was able to find an error in my code - it turned out to be a missed Expanded. Now it works about as I would like. There is not enough smoothness, but so far I do not know what to do with it. If you have any ideas, I will be grateful!
Using Animated Containers might help!
Try This;