I have this design that I'm creating on Flutter:
Here is what I managed to do so far:
I'll provided you my code and I ll explain it and finally I'll try to explain the error that I'm having. First here is my code:
class CheckoutPage extends StatefulWidget {
const CheckoutPage({super.key});
@override
State<CheckoutPage> createState() => _CheckoutPageState();
}
class _CheckoutPageState extends State<CheckoutPage>
with TickerProviderStateMixin {
Map<String, bool> isActive = {
'Quantity': true,
'Payment': false,
'Review': false,
};
Map<String, bool> isDone = {
'Quantity': false,
'Payment': false,
'Review': false,
};
@override
Widget build(BuildContext context) {
order = ModalRoute.of(context)!.settings.arguments as Order;
final PageController pageViewController = PageController();
final double height = MediaQuery.of(context).size.height;
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
final TextEditingController nameController = TextEditingController();
final TextEditingController phoneNumberController = TextEditingController();
final TextEditingController addressController = TextEditingController();
return Scaffold(
resizeToAvoidBottomInset: false,
body: Stack(
children: [
Column(
children: [
SizedBox(
height: height * 0.16,
child: PageViewIndicator(isActive: isActive, isDone: isDone),
),
SizedBox(
height: height * 0.84,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 18,
),
child: PageView(
controller: pageViewController,
children: [
QuantityStepWidget(
formKey: formKey,
addressController: addressController,
nameController: nameController,
phoneNumberController: phoneNumberController,
),
const PaymentStepWidget(),
const ReviewStepWidget(),
],
),
),
),
],
),
const BackButtonWidget(),
],
),
);
}
}
This code is mainly composed of a Scaffold
, it's child is a Stack
that stacks a back button (BackButton
) on tap of a Column
. This column has two containers SizedBox
; the first one takes 16% of the height of the screen, and the second one takes 84% which makes a total of 100%. Inside the first SizedBox
I defined a PageViewIndicator
widget which is a Container
that holds a Row
that holds 3 HeadingPageViewItemWidget
like this:
class PageViewIndicator extends StatelessWidget {
final Map<String, bool> isActive;
final Map<String, bool> isDone;
const PageViewIndicator({
super.key,
required this.isActive,
required this.isDone,
});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(color: AppColors.borderColor),
),
),
child: Padding(
padding: const EdgeInsets.only(left: 8.0, right: 8.0, bottom: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
HeadingPageViewItemWidget(
number: '1',
title: 'Quantity',
isActive: isActive['Quantity']!,
isDone: isDone['Quantity']!,
),
HeadingPageViewItemWidget(
number: '2',
title: 'Payment',
isActive: isActive['Payment']!,
isDone: isDone['Payment']!,
),
HeadingPageViewItemWidget(
number: '3',
title: 'Review',
isActive: isActive['Review']!,
isDone: isDone['Review']!,
),
],
),
),
);
}
}
The HeadingPageViewItemWidget
is another Row
that holds a CircleAvatar
and a Text
widget, like this:
class HeadingPageViewItemWidget extends StatelessWidget {
final String number;
final String title;
final bool isDone;
final bool isActive;
const HeadingPageViewItemWidget({
Key? key,
required this.number,
required this.title,
this.isDone = false,
this.isActive = false,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return SizedBox(
width: 100,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CircleAvatar(
backgroundColor: _getColor(isDone: isDone, isActive: isActive),
radius: 15,
child: _getIconOrNumber(isDone),
),
SizedBox(
width: 60,
child: Text(title, style: kCaption0),
),
],
),
);
}
}
Inside the second SizedBox
I have defined a PageView
that has 3 children, QuantityStepWidget
, PaymentStepWidget
and ReviewStepWidget
, these last two are just PlaceHolder
s for the moment. The QuantityStepWidget
is defined like this:
class QuantityStepWidget extends StatelessWidget {
final GlobalKey<FormState> formKey;
final TextEditingController nameController;
final TextEditingController phoneNumberController;
final TextEditingController addressController;
const QuantityStepWidget(
{super.key,
required this.formKey,
required this.nameController,
required this.phoneNumberController,
required this.addressController});
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: SizedBox(
height: MediaQuery.of(context).size.height * 0.8,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
fit: FlexFit.loose,
child: SizedBox(
height: MediaQuery.of(context).size.height * 0.05,
child: const TitleWidget(title: 'Quantity')),
),
Flexible(
flex: 3,
fit: FlexFit.loose,
child: SizedBox(
height: MediaQuery.of(context).size.height * 0.35,
width: double.infinity,
child: MedicationCheckoutQuantityWidget(order: order),
),
),
Flexible(
flex: 3,
fit: FlexFit.loose,
child: SizedBox(
height: MediaQuery.of(context).size.height * 0.4,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Confirm your information',
style: kH3TitleStyle,
),
const SizedBox(height: 10),
Flexible(
fit: FlexFit.loose,
child: SizedBox(
height: 200,
child: Form(
key: formKey,
child: Column(
children: [
Flexible(
flex: 1,
fit: FlexFit.loose,
child:
TextFormFieldUserInformationsConfirmationWidget(
type: FormType.name,
controller: nameController,
),
),
Flexible(
flex: 1,
fit: FlexFit.loose,
child:
TextFormFieldUserInformationsConfirmationWidget(
type: FormType.phoneNumber,
controller: phoneNumberController,
),
),
Flexible(
flex: 1,
fit: FlexFit.loose,
child:
TextFormFieldUserInformationsConfirmationWidget(
type: FormType.address,
controller: addressController,
),
),
],
),
),
),
),
const SizedBox(height: 20),
ButtonWidget(
onPressed: () {
print('lol');
if (formKey.currentState!.validate()) {
print(nameController.text);
print(phoneNumberController.text);
print(addressController.text);
}
},
text: 'Confirm'),
],
),
),
)
],
),
),
);
}
}
It consists of a SingleChildScrollView
and inside it there is a Column
which holds thre Flexible
widgets; the first one is the 'Quantity' title, the second one is the two cards of the medication name and quantity, and the third one is the form.
What I want is to make the form go up when the keyboard gets triggered, but it doesn't want to do that, all it does is to show the keyboard and then close it instanly, and it doesn't give me the time to add text, as soon as the keyboard opens it closes