mirror of
https://github.com/Iconica-Development/flutter_form_wizard.git
synced 2025-05-19 19:03:47 +02:00
feat: improved example
This commit is contained in:
parent
11c8fcde73
commit
2741be487c
14 changed files with 167 additions and 148 deletions
|
@ -19,6 +19,8 @@ class AgePage {
|
||||||
amountOfPages: amountOfPages,
|
amountOfPages: amountOfPages,
|
||||||
shellFormWidgets: [
|
shellFormWidgets: [
|
||||||
ShellFormInputNumberPicker(
|
ShellFormInputNumberPicker(
|
||||||
|
minValue: 12,
|
||||||
|
maxValue: 120,
|
||||||
controller: ShellFormInputNumberPickerController(
|
controller: ShellFormInputNumberPickerController(
|
||||||
id: "age",
|
id: "age",
|
||||||
checkPageTitle: (dynamic amount) {
|
checkPageTitle: (dynamic amount) {
|
||||||
|
|
|
@ -11,14 +11,14 @@ class CheckPageExample {
|
||||||
return CheckPage(
|
return CheckPage(
|
||||||
title: Container(
|
title: Container(
|
||||||
margin: const EdgeInsets.only(
|
margin: const EdgeInsets.only(
|
||||||
top: 60,
|
top: 70,
|
||||||
bottom: 10,
|
bottom: 10,
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 40),
|
padding: const EdgeInsets.symmetric(horizontal: 40),
|
||||||
child: Text(
|
child: const Text(
|
||||||
checkPageText,
|
"Check answers",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: fontSize,
|
fontSize: 25,
|
||||||
fontWeight: FontWeight.w900,
|
fontWeight: FontWeight.w900,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -30,7 +30,7 @@ class CheckPageExample {
|
||||||
await onPressed();
|
await onPressed();
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
width: size.width * 0.9,
|
width: MediaQuery.of(context).size.width * 0.9,
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
top: 18,
|
top: 18,
|
||||||
bottom: 16,
|
bottom: 16,
|
||||||
|
@ -80,7 +80,7 @@ class CheckPageExample {
|
||||||
if (description != null)
|
if (description != null)
|
||||||
Text(
|
Text(
|
||||||
description,
|
description,
|
||||||
style: TextStyle(fontSize: fontSize / 1.3),
|
style: const TextStyle(fontSize: 16),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -18,6 +18,19 @@ class NamePage {
|
||||||
amountOfPages: amountOfPages,
|
amountOfPages: amountOfPages,
|
||||||
title: "Please enter your name",
|
title: "Please enter your name",
|
||||||
shellFormWidgets: [
|
shellFormWidgets: [
|
||||||
|
// Padding(
|
||||||
|
// padding: const EdgeInsets.fromLTRB(40, 0, 40, 40),
|
||||||
|
// child: ShellFormInputPlainText(
|
||||||
|
// label: const Text("Name"),
|
||||||
|
// controller: ShellFormInputPlainTextController(
|
||||||
|
// mandatory: true,
|
||||||
|
// id: "name",
|
||||||
|
// checkPageTitle: (dynamic name) {
|
||||||
|
// return "Name: $name";
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(40, 0, 40, 40),
|
padding: const EdgeInsets.fromLTRB(40, 0, 40, 40),
|
||||||
child: ShellFormInputPlainText(
|
child: ShellFormInputPlainText(
|
||||||
|
@ -31,19 +44,19 @@ class NamePage {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
// Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(40, 0, 40, 0),
|
// padding: const EdgeInsets.fromLTRB(40, 0, 40, 0),
|
||||||
child: ShellFormInputPlainText(
|
// child: ShellFormInputPlainText(
|
||||||
label: const Text("Last Name"),
|
// label: const Text("Last Name"),
|
||||||
controller: ShellFormInputPlainTextController(
|
// controller: ShellFormInputPlainTextController(
|
||||||
mandatory: true,
|
// mandatory: true,
|
||||||
id: "lastName",
|
// id: "lastName",
|
||||||
checkPageTitle: (dynamic lastName) {
|
// checkPageTitle: (dynamic lastName) {
|
||||||
return "Last Name: $lastName";
|
// return "Last Name: $lastName";
|
||||||
},
|
// },
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -24,85 +24,88 @@ class _FormExampleState extends ConsumerState<FormExample> {
|
||||||
var size = MediaQuery.of(context).size;
|
var size = MediaQuery.of(context).size;
|
||||||
var fontSize = size.height / 40;
|
var fontSize = size.height / 40;
|
||||||
|
|
||||||
return Scaffold(
|
return GestureDetector(
|
||||||
body: Center(
|
onTap: () => FocusScope.of(context).unfocus(),
|
||||||
child: ShellForm(
|
child: Scaffold(
|
||||||
formController: formController,
|
body: Center(
|
||||||
options: ShellFormOptions(
|
child: ShellForm(
|
||||||
onFinished: (Map<int, Map<String, dynamic>> results) {
|
formController: formController,
|
||||||
print("Totale resultaten: $results");
|
options: ShellFormOptions(
|
||||||
Navigator.of(context).pushNamed('/thanks');
|
onFinished: (Map<int, Map<String, dynamic>> results) {
|
||||||
},
|
print("Final full results: $results");
|
||||||
onNext: (int pageNumber, Map<String, dynamic> results) {
|
Navigator.of(context).pushNamed('/thanks');
|
||||||
print("Resultaten pagina $pageNumber: $results");
|
},
|
||||||
},
|
onNext: (int pageNumber, Map<String, dynamic> results) {
|
||||||
nextButton: (int pageNumber, bool checkingPages) {
|
print("Results page $pageNumber: $results");
|
||||||
return Align(
|
},
|
||||||
alignment: Alignment.bottomCenter,
|
nextButton: (int pageNumber, bool checkingPages) {
|
||||||
child: Padding(
|
return Align(
|
||||||
padding: EdgeInsets.only(
|
alignment: Alignment.bottomCenter,
|
||||||
bottom: size.height / 20,
|
child: Padding(
|
||||||
),
|
padding: EdgeInsets.only(
|
||||||
child: SizedBox(
|
bottom: size.height * 0.05,
|
||||||
height: size.height / 15,
|
),
|
||||||
width: size.width / 1.5,
|
child: SizedBox(
|
||||||
child: ElevatedButton(
|
height: size.height * 0.07,
|
||||||
style: ElevatedButton.styleFrom(
|
width: size.width * 0.7,
|
||||||
shape: RoundedRectangleBorder(
|
child: ElevatedButton(
|
||||||
borderRadius: BorderRadius.circular(5),
|
style: ElevatedButton.styleFrom(
|
||||||
),
|
shape: RoundedRectangleBorder(
|
||||||
backgroundColor: Colors.black,
|
borderRadius: BorderRadius.circular(5),
|
||||||
textStyle: TextStyle(
|
),
|
||||||
fontSize: fontSize,
|
backgroundColor: Colors.black,
|
||||||
fontWeight: FontWeight.w600,
|
textStyle: TextStyle(
|
||||||
color: Colors.white,
|
fontSize: fontSize,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
onPressed: () {
|
||||||
|
formController.autoNextStep();
|
||||||
|
},
|
||||||
|
child: Text(checkingPages ? "Save" : "Next Page"),
|
||||||
),
|
),
|
||||||
onPressed: () {
|
|
||||||
formController.autoNextStep();
|
|
||||||
},
|
|
||||||
child: Text(checkingPages ? "Save" : "Next Page"),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
},
|
||||||
},
|
backButton: (int pageNumber, bool checkingPages, int pageAmount) {
|
||||||
backButton: (int pageNumber, bool checkingPages, int pageAmount) {
|
if (pageNumber != 0) {
|
||||||
if (pageNumber != 0) {
|
if (!checkingPages || pageNumber >= pageAmount) {
|
||||||
if (!checkingPages || pageNumber >= pageAmount) {
|
return Align(
|
||||||
return Align(
|
alignment: Alignment.topLeft,
|
||||||
alignment: Alignment.topLeft,
|
child: Container(
|
||||||
child: Container(
|
margin: EdgeInsets.only(
|
||||||
margin: EdgeInsets.only(
|
top: size.height * 0.045,
|
||||||
top: size.height / 20,
|
left: size.width * 0.07,
|
||||||
left: size.width / 15,
|
),
|
||||||
),
|
width: size.width * 0.08,
|
||||||
width: 30,
|
height: size.width * 0.08,
|
||||||
height: 30,
|
decoration: BoxDecoration(
|
||||||
decoration: BoxDecoration(
|
borderRadius: BorderRadius.circular(90),
|
||||||
borderRadius: BorderRadius.circular(90),
|
color: const Color(0xFFD8D8D8).withOpacity(0.50),
|
||||||
color: const Color(0xFFD8D8D8).withOpacity(0.50),
|
),
|
||||||
),
|
child: IconButton(
|
||||||
child: IconButton(
|
padding: EdgeInsets.zero,
|
||||||
padding: EdgeInsets.zero,
|
splashRadius: size.width * 0.06,
|
||||||
splashRadius: 29,
|
onPressed: () {
|
||||||
onPressed: () {
|
formController.previousStep();
|
||||||
formController.previousStep();
|
},
|
||||||
},
|
icon: const Icon(Icons.chevron_left),
|
||||||
icon: const Icon(Icons.chevron_left),
|
)),
|
||||||
)),
|
);
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
}
|
return Container();
|
||||||
return Container();
|
},
|
||||||
},
|
pages: [
|
||||||
pages: [
|
AgePage().returnPage(size, fontSize, 1, 3),
|
||||||
AgePage().returnPage(size, fontSize, 1, 3),
|
// NamePage().returnPage(size, fontSize, 2, 3),
|
||||||
NamePage().returnPage(size, fontSize, 2, 3),
|
CarouselPage().returnPage(size, fontSize, 3, 3),
|
||||||
CarouselPage().returnPage(size, fontSize, 3, 3),
|
],
|
||||||
],
|
checkPage: CheckPageExample()
|
||||||
checkPage: CheckPageExample()
|
.showCheckpage(context, size, fontSize, checkPageText),
|
||||||
.showCheckpage(context, size, fontSize, checkPageText),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -20,50 +20,47 @@ class TemplatePage extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return Column(
|
||||||
onTap: () => FocusScope.of(context).unfocus(),
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
child: Column(
|
children: [
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
Align(
|
||||||
children: [
|
alignment: Alignment.centerLeft,
|
||||||
Align(
|
child: Padding(
|
||||||
alignment: Alignment.centerLeft,
|
padding: EdgeInsets.symmetric(horizontal: size.width / 10),
|
||||||
child: Padding(
|
child: Column(
|
||||||
padding: EdgeInsets.symmetric(horizontal: size.width / 10),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
child: Column(
|
children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
SizedBox(
|
||||||
children: [
|
height: size.height / 10,
|
||||||
SizedBox(
|
),
|
||||||
height: size.height / 10,
|
Text(
|
||||||
|
"$pageNumber / $amountOfPages",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: fontSize,
|
||||||
),
|
),
|
||||||
Text(
|
),
|
||||||
"$pageNumber / $amountOfPages",
|
SizedBox(
|
||||||
style: TextStyle(
|
height: size.height / 80,
|
||||||
fontSize: fontSize,
|
),
|
||||||
),
|
Text(
|
||||||
|
title,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: fontSize,
|
||||||
|
fontWeight: FontWeight.w900,
|
||||||
),
|
),
|
||||||
SizedBox(
|
),
|
||||||
height: size.height / 80,
|
],
|
||||||
),
|
|
||||||
Text(
|
|
||||||
title,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: fontSize,
|
|
||||||
fontWeight: FontWeight.w900,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
),
|
||||||
for (var widget in shellFormWidgets) ...[
|
const Spacer(),
|
||||||
widget,
|
for (var widget in shellFormWidgets) ...[
|
||||||
],
|
widget,
|
||||||
const Spacer(
|
|
||||||
flex: 2,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
const Spacer(
|
||||||
|
flex: 2,
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,8 +43,8 @@ class ShellFormPage {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// CheckPage is used to set a check page at the end of a [ShellForm].
|
/// [CheckPage] is used to set a check page at the end of a [ShellForm].
|
||||||
/// A CheckPage is a page where the user can check all input values before commiting.
|
/// A [CheckPage] is a page where the user can check all input values before commiting.
|
||||||
///
|
///
|
||||||
/// [title] is the widget shown at the top of the page.
|
/// [title] is the widget shown at the top of the page.
|
||||||
///
|
///
|
||||||
|
|
|
@ -25,7 +25,7 @@ import 'src/utils/formstate.dart' as fs;
|
||||||
/// // print(results);
|
/// // print(results);
|
||||||
/// },
|
/// },
|
||||||
/// onNext: (int pageNumber, Map<String, dynamic> results) {
|
/// onNext: (int pageNumber, Map<String, dynamic> results) {
|
||||||
/// // print("Resultaten pagina $pageNumber: $results");
|
/// // print("Results page $pageNumber: $results");
|
||||||
/// },
|
/// },
|
||||||
/// nextButton: (int pageNumber, bool checkingPages) {
|
/// nextButton: (int pageNumber, bool checkingPages) {
|
||||||
/// return Align(
|
/// return Align(
|
||||||
|
@ -38,7 +38,7 @@ import 'src/utils/formstate.dart' as fs;
|
||||||
/// onPressed: () {
|
/// onPressed: () {
|
||||||
/// shellFormController.autoNextStep();
|
/// shellFormController.autoNextStep();
|
||||||
/// },
|
/// },
|
||||||
/// child: Text(checkingPages ? "Opslaan" : "Volgende stap"),
|
/// child: Text(checkingPages ? "Save" : "Next Page"),
|
||||||
/// ),
|
/// ),
|
||||||
/// ),
|
/// ),
|
||||||
/// );
|
/// );
|
||||||
|
@ -100,7 +100,7 @@ import 'src/utils/formstate.dart' as fs;
|
||||||
/// ],
|
/// ],
|
||||||
/// checkPage: CheckPage(
|
/// checkPage: CheckPage(
|
||||||
/// title: const Text(
|
/// title: const Text(
|
||||||
/// "Hier zijn je wensen voor het afscheidsfeestje",
|
/// "All entered info: ",
|
||||||
/// style: TextStyle(
|
/// style: TextStyle(
|
||||||
/// fontSize: 25,
|
/// fontSize: 25,
|
||||||
/// fontWeight: FontWeight.w900,
|
/// fontWeight: FontWeight.w900,
|
||||||
|
|
|
@ -73,7 +73,7 @@ class CarouselSliderState extends State<CarouselSlider>
|
||||||
|
|
||||||
PageController? pageController;
|
PageController? pageController;
|
||||||
|
|
||||||
/// [mode] is related to why the page is being changed
|
/// [mode] is related to why the page is being changed.
|
||||||
CarouselPageChangedReason mode = CarouselPageChangedReason.controller;
|
CarouselPageChangedReason mode = CarouselPageChangedReason.controller;
|
||||||
|
|
||||||
CarouselSliderState();
|
CarouselSliderState();
|
||||||
|
@ -87,14 +87,14 @@ class CarouselSliderState extends State<CarouselSlider>
|
||||||
carouselState!.options = options;
|
carouselState!.options = options;
|
||||||
carouselState!.itemCount = widget.itemCount;
|
carouselState!.itemCount = widget.itemCount;
|
||||||
|
|
||||||
// pageController needs to be re-initialized to respond to state changes
|
/// [pageController] needs to be re-initialized to respond to state changes.
|
||||||
pageController = PageController(
|
pageController = PageController(
|
||||||
viewportFraction: options.viewportFraction,
|
viewportFraction: options.viewportFraction,
|
||||||
initialPage: carouselState!.realPage,
|
initialPage: carouselState!.realPage,
|
||||||
);
|
);
|
||||||
carouselState!.pageController = pageController;
|
carouselState!.pageController = pageController;
|
||||||
|
|
||||||
// handle autoplay when state changes
|
/// handle autoplay when state changes
|
||||||
handleAutoPlay();
|
handleAutoPlay();
|
||||||
|
|
||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
|
@ -296,13 +296,13 @@ class CarouselSliderState extends State<CarouselSlider>
|
||||||
: widget.itemBuilder!(context, index, idx),
|
: widget.itemBuilder!(context, index, idx),
|
||||||
builder: (BuildContext context, child) {
|
builder: (BuildContext context, child) {
|
||||||
double distortionValue = 1.0;
|
double distortionValue = 1.0;
|
||||||
// if `enlargeCenterPage` is true, we must calculate the carousel item's height
|
// if [enlargeCenterPage] is true, we must calculate the carousel item's height
|
||||||
// to display the visual effect
|
// to display the visual effect
|
||||||
|
|
||||||
if (widget.options.enlargeCenterPage != null &&
|
if (widget.options.enlargeCenterPage != null &&
|
||||||
widget.options.enlargeCenterPage == true) {
|
widget.options.enlargeCenterPage == true) {
|
||||||
// pageController.page can only be accessed after the first build,
|
// [pageController.page] can only be accessed after the first build,
|
||||||
// so in the first build we calculate the itemoffset manually
|
// so in the first build we calculate the [itemOffset] manually
|
||||||
double itemOffset = 0;
|
double itemOffset = 0;
|
||||||
var position = carouselState?.pageController?.position;
|
var position = carouselState?.pageController?.position;
|
||||||
if (position != null &&
|
if (position != null &&
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
///
|
///
|
||||||
/// For example; We have a Carousel of 10000(simulating infinity) but only 6 images.
|
/// For example; We have a Carousel of 10000(simulating infinity) but only 6 images.
|
||||||
/// We need to repeat the images to give the illusion of a never ending stream.
|
/// We need to repeat the images to give the illusion of a never ending stream.
|
||||||
/// By calling _getRealIndex with position and base we get an offset.
|
/// By calling [getRealIndex] with position and base we get an offset.
|
||||||
/// This offset modulo our length, 6, will return a number between 0 and 5, which represent the image
|
/// This offset modulo our length, 6, will return a number between 0 and 5, which represent the image
|
||||||
/// to be placed in the given position.
|
/// to be placed in the given position.
|
||||||
int getRealIndex(int position, int base, int? length) {
|
int getRealIndex(int position, int base, int? length) {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import 'carousel_form.dart';
|
||||||
|
|
||||||
/// Input for a carousel of items used in a [ShellForm].
|
/// Input for a carousel of items used in a [ShellForm].
|
||||||
///
|
///
|
||||||
/// items will be the [Widget]s to be displayed in the carousel.
|
/// [items] will be the [Widget]s to be displayed in the carousel.
|
||||||
///
|
///
|
||||||
/// Standard controller is [ShellFormInputCarouselController].
|
/// Standard controller is [ShellFormInputCarouselController].
|
||||||
class ShellFormInputCarousel extends ShellFormInputWidget {
|
class ShellFormInputCarousel extends ShellFormInputWidget {
|
||||||
|
@ -37,7 +37,7 @@ class ShellFormInputCarousel extends ShellFormInputWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Controller for the carousel used by a [ShellFormInputWidget] used in a [ShellFrom].
|
/// Controller for the carousel used by a [ShellFormInputWidget] used in a [ShellForm].
|
||||||
///
|
///
|
||||||
/// Mainly used by [ShellFormInputCarousel].
|
/// Mainly used by [ShellFormInputCarousel].
|
||||||
class ShellFormInputCarouselController
|
class ShellFormInputCarouselController
|
||||||
|
|
|
@ -39,7 +39,7 @@ class ShellFormInputEmail extends ShellFormInputWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Controller for emails used by a [ShellFormInputWidget] used in a [ShellFrom].
|
/// Controller for emails used by a [ShellFormInputWidget] used in a [ShellForm].
|
||||||
///
|
///
|
||||||
/// Mainly used by [ShellFormInputEmail].
|
/// Mainly used by [ShellFormInputEmail].
|
||||||
class ShellFormInputEmailController
|
class ShellFormInputEmailController
|
||||||
|
|
|
@ -26,9 +26,11 @@ class ShellFormInputNumberPicker extends ShellFormInputWidget {
|
||||||
super.registerController(context);
|
super.registerController(context);
|
||||||
|
|
||||||
return NumberPickerFormField(
|
return NumberPickerFormField(
|
||||||
|
minValue: minValue,
|
||||||
|
maxValue: maxValue,
|
||||||
onSaved: (value) => controller.onSaved(value),
|
onSaved: (value) => controller.onSaved(value),
|
||||||
validator: (value) => controller.onValidate(value, _),
|
validator: (value) => controller.onValidate(value, _),
|
||||||
initialValue: controller.value ?? 0,
|
initialValue: controller.value ?? minValue,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
/// Creates a slider with the given input parameters
|
||||||
class SliderFormField extends FormField<double> {
|
class SliderFormField extends FormField<double> {
|
||||||
SliderFormField({
|
SliderFormField({
|
||||||
Key? key,
|
Key? key,
|
||||||
|
|
1
test/flutter_form_tests.dart
Normal file
1
test/flutter_form_tests.dart
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
Loading…
Reference in a new issue