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
Using Animated Containers might help! Try This;