Problem
I have tried SingleChildListView and ListView widgets but the children end up spanning full width. I need the widgets to not exceed a maxWidth of 850px. Using a container with a width:850 or a BoxContraints(maxWidth:850) does not apply to the children inside the list view.
Desired Outcome
The form should be centered on the page but the scrollbar should be as far right as possible. If the user's mouse is on the form or in the white space to the left/right of the form, the user should retain the ability to scroll up/down.
SFUScaffold(
body: Center(
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Container(
constraints:
BoxConstraints(maxWidth: ThemeManager.getTheme().maxWidth),
child: Scrollbar(
isAlwaysShown: true,
controller: _scrollbar,
child: SingleChildScrollView(
controller: _scrollbar,
padding: EdgeInsets.all(15),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SFUSizedBox(height: 5),
Text(
"Secure File Upload Form",
style: ThemeManager.getTheme().formTitleStyle,
),
SFUSizedBox(height: 5),
Text(
"Quickly upload and send documents directly to the ${ThemeManager.getTheme().clientName} team",
style: ThemeManager.getTheme().formSubtitleStyle,
),
SFUSizedBox(height: 5),
Divider(),
SFUSizedBox(height: 5),
BlocBuilder<FormCubit, FormCubitState>(
builder: (context, state) {
return Visibility(
visible: state.hasErrors,
child: FittedBox(
child: Row(
children: [
Icon(
LineIcons.exclamationCircle,
color: ThemeManager.getTheme().errorColor,
size: 30,
),
Text(
"Your form was not sent. Please correct the issues below and try again.",
style: ThemeManager.getTheme().errorStyle,
),
],
),
),
);
}),
Text(
"Tell us who you are",
style: ThemeManager.getTheme().formSectionTitleStyle,
),
SizedBox(height: ThemeManager.getTheme().px2pt(10)),
isLargeScreen
? _BuildNameLayout(direction: Axis.horizontal)
: _BuildNameLayout(direction: Axis.vertical),
SFUSizedBox(height: 5),
BlocBuilder<FormCubit, FormCubitState>(
builder: (context, state) {
return Container(
width: isLargeScreen
? ThemeManager.getTheme().px2pt(400)
: ThemeManager.getTheme().px2pt(600),
child: PXFormField(
label: "Member ID:",
optional: true,
initalValue: state.formFields["member_id"] ?? "",
onChanged: (value) {
context
.read<FormCubit>()
.updateFormField('member_id', value);
},
),
);
},
),
SizedBox(height: ThemeManager.getTheme().px2pt(20)),
Divider(),
SizedBox(height: ThemeManager.getTheme().px2pt(10)),
Text(
"Share your contact details",
style: ThemeManager.getTheme().formSectionTitleStyle,
),
SizedBox(height: ThemeManager.getTheme().px2pt(10)),
BlocBuilder<FormCubit, FormCubitState>(
builder: (context, state) {
return Container(
width: isLargeScreen
? ThemeManager.getTheme().px2pt(400)
: ThemeManager.getTheme().px2pt(600),
child: PXFormField(
initalValue: state.formFields["email"] ?? "",
label: "Email: *",
optional: false,
onChanged: (value) {
context
.read<FormCubit>()
.updateFormField('email', value);
},
validator: (value) {
if (!FieldValidator.validateEmail(value)) {
return "Please provide a valid email address.";
} else if (state.formErrors
.containsKey("email")) {
return state.formErrors['email'];
}
return null;
}),
);
},
),
SizedBox(height: ThemeManager.getTheme().px2pt(10)),
BlocBuilder<FormCubit, FormCubitState>(
builder: (context, state) {
return Container(
width: isLargeScreen
? ThemeManager.getTheme().px2pt(400)
: ThemeManager.getTheme().px2pt(600),
child: PXFormField(
initalValue:
state.formFields["phone_number"] ?? "",
label: "Phone Number:",
optional: true,
onChanged: (value) {
context
.read<FormCubit>()
.updateFormField('phone_number', value);
},
),
);
},
),
SizedBox(height: ThemeManager.getTheme().px2pt(20)),
Divider(
thickness: 1.0,
),
SizedBox(height: ThemeManager.getTheme().px2pt(10)),
Text(
"Select the files that you would like to send",
style: ThemeManager.getTheme().formSectionTitleStyle,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
BlocBuilder<FormCubit, FormCubitState>(
builder: (context, state) {
return Text(
state.formErrors['files'] != null
? state.formErrors['files'].toString()
: "",
style: ThemeManager.getTheme().errorStyle,
);
},
),
],
),
Text(
"Upload Your Files: *",
style: ThemeManager.getTheme().formLabelStyle,
),
SizedBox(
height: ThemeManager.getTheme().px2pt(15),
),
UploadFilesComponent(),
Container(
child: BlocBuilder<FormCubit, FormCubitState>(
builder: (context, state) {
return Column(
children: new List.generate(
state.files.length,
(index) => FileCard(
//Add Progress value here for styling....
file: state.files[index],
fileIndex: index),
),
);
},
),
),
SizedBox(height: ThemeManager.getTheme().px2pt(10)),
Divider(),
SizedBox(height: ThemeManager.getTheme().px2pt(10)),
Text(
"Provide any background or instructions for the files you're sending",
style: ThemeManager.getTheme().formSectionTitleStyle),
SFUSizedBox(height: 20),
RichText(
text: TextSpan(
text: "Additional Information:",
style: ThemeManager.getTheme().formLabelStyle,
children: <TextSpan>[
TextSpan(
text: " (optional)",
style: ThemeManager.getTheme()
.formOptionalLabelStyle,
)
]),
),
SFUSizedBox(height: 30),
BlocBuilder<FormCubit, FormCubitState>(
builder: (context, state) {
return TextFormField(
keyboardType: TextInputType.multiline,
maxLines: null,
minLines: 4,
onChanged: (value) {
context.read<FormCubit>().updateFormField(
'additional_information', value);
},
);
},
),
SFUSizedBox(height: 30),
Center(
child: Container(
width: ThemeManager.getTheme().px2pt(300),
height: ThemeManager.getTheme().px2pt(50),
child: BlocBuilder<FormCubit, FormCubitState>(
builder: (context, state) {
return DarkOutlinedButton(
buttonText: "Send Your Files",
onpressed: () {
context.read<FormCubit>().validateForm();
_formKey.currentState!.validate();
if (!state.hasErrors) {
Navigator.pushNamedAndRemoveUntil(context,
"/submission-details", (_) => false);
}
},
);
},
),
),
),
SFUSizedBox(height: 30),
Divider(),
SFUSizedBox(height: 30),
Center(
child: Image.asset("images/powered_by_pensionx.png")),
Center(
child: TextButton(
onPressed: () async {
await showDialog(
context: context,
builder: (_) => ReportProblemModal(),
);
},
child: Text(
"Report a Problem",
style: TextStyle(
color: Theme.of(context).primaryColor),
),
),
)
],
),
),
),
),
),
),
),
);
class SFUSizedBox extends StatelessWidget {
final double height;
const SFUSizedBox({
required this.height,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return SizedBox(height: ThemeManager.getTheme().px2pt(height));
}
}
class _BuildNameLayout extends StatelessWidget {
final Axis direction;
_BuildNameLayout({required this.direction});
@override
Widget build(BuildContext context) {
List<Widget> content = [
Expanded(
flex: this.direction == Axis.horizontal ? 3 : 0,
child: BlocBuilder<FormCubit, FormCubitState>(
builder: (context, state) {
return PXFormField(
initalValue: state.formFields["first_name"] ?? "",
label: "First Name: *",
optional: false,
validator: (value) {
if (state.formErrors.containsKey("first_name")) {
return state.formErrors['first_name'];
}
return null;
},
onChanged: (value) {
context.read<FormCubit>().updateFormField('first_name', value);
},
);
},
),
),
SizedBox(width: 10),
Expanded(
flex: this.direction == Axis.horizontal ? 2 : 0,
child: BlocBuilder<FormCubit, FormCubitState>(
builder: (context, state) {
return PXFormField(
initalValue: state.formFields["middle_initial"] ?? "",
label: "Middle Initial:",
optional: false,
onChanged: (value) {
context
.read<FormCubit>()
.updateFormField('middle_initial', value);
},
);
},
),
),
SizedBox(width: 10),
Expanded(
flex: this.direction == Axis.horizontal ? 3 : 0,
child: BlocBuilder<FormCubit, FormCubitState>(
builder: (context, state) {
return PXFormField(
initalValue: state.formFields["last_name"] ?? "",
label: "Last Name: *",
optional: false,
onChanged: (value) {
context.read<FormCubit>().updateFormField('last_name', value);
},
validator: (value) {
if (state.formErrors.containsKey("last_name")) {
return state.formErrors['last_name'];
}
return null;
},
);
},
),
),
];
if (this.direction == Axis.horizontal) {
return Container(
width: ThemeManager.getTheme().maxWidth,
child: Row(children: content));
}
if (this.direction == Axis.vertical) {
return Column(
mainAxisSize: MainAxisSize.min,
children: content,
);
}
// This shoudln't ever happen.
throw new Exception(
'Direction must be set to Axis.horizontal or Axis.vertical');
}
}
The image above has the following problems:
- The white space to the left/right doesn't allow the user to scroll
- The scroll bar needs to be as far to right as possible.
- The form appears to be embedded into the page but I want it to feel like an entire page.