added multimedia support and STT. STT and photos are have bugs but are functional. file upload is conceptualized but will require additional backend work. video is on hold for now.
This commit is contained in:
@@ -0,0 +1,496 @@
|
||||
import 'package:flutterflow_ui/flutterflow_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_animate/flutter_animate.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../components/support_form_model.dart';
|
||||
export '../components/support_form_model.dart';
|
||||
|
||||
import 'package:app/theme.dart';
|
||||
|
||||
class SupportFormWidget extends StatefulWidget {
|
||||
const SupportFormWidget({super.key});
|
||||
|
||||
@override
|
||||
State<SupportFormWidget> createState() => _SupportFormWidgetState();
|
||||
}
|
||||
|
||||
class _SupportFormWidgetState extends State<SupportFormWidget>
|
||||
with TickerProviderStateMixin {
|
||||
late SupportFormModel _model;
|
||||
|
||||
final scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
|
||||
final animationsMap = <String, AnimationInfo>{};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_model = createModel(context, () => SupportFormModel());
|
||||
|
||||
_model.textController1 ??= TextEditingController();
|
||||
_model.textFieldFocusNode1 ??= FocusNode();
|
||||
|
||||
_model.textController2 ??= TextEditingController();
|
||||
_model.textFieldFocusNode2 ??= FocusNode();
|
||||
|
||||
_model.textController3 ??= TextEditingController();
|
||||
_model.textFieldFocusNode3 ??= FocusNode();
|
||||
|
||||
animationsMap.addAll({
|
||||
'containerOnPageLoadAnimation1': AnimationInfo(
|
||||
trigger: AnimationTrigger.onPageLoad,
|
||||
effectsBuilder: () => [
|
||||
FadeEffect(
|
||||
curve: Curves.easeInOut,
|
||||
delay: 0.0.ms,
|
||||
duration: 600.0.ms,
|
||||
begin: 0.0,
|
||||
end: 1.0,
|
||||
),
|
||||
MoveEffect(
|
||||
curve: Curves.easeInOut,
|
||||
delay: 0.0.ms,
|
||||
duration: 600.0.ms,
|
||||
begin: const Offset(0.0, 110.0),
|
||||
end: const Offset(0.0, 0.0),
|
||||
),
|
||||
],
|
||||
),
|
||||
'containerOnPageLoadAnimation2': AnimationInfo(
|
||||
trigger: AnimationTrigger.onPageLoad,
|
||||
effectsBuilder: () => [
|
||||
FadeEffect(
|
||||
curve: Curves.easeInOut,
|
||||
delay: 0.0.ms,
|
||||
duration: 600.0.ms,
|
||||
begin: 0.0,
|
||||
end: 1.0,
|
||||
),
|
||||
MoveEffect(
|
||||
curve: Curves.easeInOut,
|
||||
delay: 0.0.ms,
|
||||
duration: 600.0.ms,
|
||||
begin: const Offset(0.0, 110.0),
|
||||
end: const Offset(0.0, 0.0),
|
||||
),
|
||||
],
|
||||
),
|
||||
'containerOnPageLoadAnimation3': AnimationInfo(
|
||||
trigger: AnimationTrigger.onPageLoad,
|
||||
effectsBuilder: () => [
|
||||
FadeEffect(
|
||||
curve: Curves.easeInOut,
|
||||
delay: 0.0.ms,
|
||||
duration: 600.0.ms,
|
||||
begin: 0.0,
|
||||
end: 1.0,
|
||||
),
|
||||
MoveEffect(
|
||||
curve: Curves.easeInOut,
|
||||
delay: 0.0.ms,
|
||||
duration: 600.0.ms,
|
||||
begin: const Offset(0.0, 110.0),
|
||||
end: const Offset(0.0, 0.0),
|
||||
),
|
||||
],
|
||||
),
|
||||
});
|
||||
setupAnimations(
|
||||
animationsMap.values.where((anim) =>
|
||||
anim.trigger == AnimationTrigger.onActionTrigger ||
|
||||
!anim.applyInitialState),
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_model.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () => _model.unfocusNode.canRequestFocus
|
||||
? FocusScope.of(context).requestFocus(_model.unfocusNode)
|
||||
: FocusScope.of(context).unfocus(),
|
||||
child: Scaffold(
|
||||
key: scaffoldKey,
|
||||
backgroundColor: AppTheme.secondaryBackground,
|
||||
appBar: AppBar(
|
||||
backgroundColor: AppTheme.secondaryBackground,
|
||||
automaticallyImplyLeading: false,
|
||||
leading: FlutterFlowIconButton(
|
||||
borderColor: Colors.transparent,
|
||||
borderRadius: 30,
|
||||
borderWidth: 1,
|
||||
buttonSize: 60,
|
||||
icon: const Icon(
|
||||
Icons.arrow_back_rounded,
|
||||
color: AppTheme.primaryTextColor,
|
||||
size: 30,
|
||||
),
|
||||
onPressed: () async {
|
||||
//TODO: context.pop();
|
||||
},
|
||||
),
|
||||
title: const Text(
|
||||
'Submit Ticket',
|
||||
style: AppTheme.titleLarge,
|
||||
),
|
||||
actions: const [],
|
||||
centerTitle: false,
|
||||
elevation: 0,
|
||||
),
|
||||
body: SafeArea(
|
||||
top: true,
|
||||
child: Align(
|
||||
alignment: const AlignmentDirectional(0, 1),
|
||||
child: Padding(
|
||||
padding: const EdgeInsetsDirectional.fromSTEB(16, 12, 16, 0),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Welcome to support',
|
||||
style: AppTheme.labelLarge,
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsetsDirectional.fromSTEB(0, 4, 0, 0),
|
||||
child: Text(
|
||||
'Submit a bug',
|
||||
style: AppTheme.headlineMedium,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0),
|
||||
child: Container(
|
||||
width: 120,
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 500,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: AppTheme
|
||||
.secondaryBackground,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: AppTheme.alternate,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: const Padding(
|
||||
padding: EdgeInsetsDirectional.fromSTEB(
|
||||
8, 16, 8, 16),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.email_outlined,
|
||||
color:
|
||||
AppTheme.primary,
|
||||
size: 36,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsetsDirectional.fromSTEB(
|
||||
0, 12, 0, 0),
|
||||
child: Text(
|
||||
'Email Us',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
).animateOnPageLoad(animationsMap[
|
||||
'containerOnPageLoadAnimation1']!),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0),
|
||||
child: Container(
|
||||
width: 120,
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 500,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: AppTheme
|
||||
.secondaryBackground,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: AppTheme.alternate,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: const Padding(
|
||||
padding: EdgeInsetsDirectional.fromSTEB(
|
||||
8, 16, 8, 16),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.search_rounded,
|
||||
color:
|
||||
AppTheme.primary,
|
||||
size: 36,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsetsDirectional.fromSTEB(
|
||||
0, 12, 0, 0),
|
||||
child: Text(
|
||||
'Search FAQs',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
).animateOnPageLoad(animationsMap[
|
||||
'containerOnPageLoadAnimation2']!),
|
||||
),
|
||||
),
|
||||
].divide(const SizedBox(width: 12)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: _model.textController1,
|
||||
focusNode: _model.textFieldFocusNode1,
|
||||
autofocus: true,
|
||||
obscureText: false,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Channel Name',
|
||||
labelStyle: AppTheme.labelMedium,
|
||||
hintStyle: AppTheme.labelMedium,
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(
|
||||
color: AppTheme.alternate,
|
||||
width: 2,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(
|
||||
color: AppTheme.primary,
|
||||
width: 2,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(
|
||||
color: AppTheme.error,
|
||||
width: 2,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
focusedErrorBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(
|
||||
color: AppTheme.error,
|
||||
width: 2,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
contentPadding: const EdgeInsetsDirectional.fromSTEB(
|
||||
16, 12, 16, 12),
|
||||
),
|
||||
style: AppTheme.bodyMedium,
|
||||
cursorColor: AppTheme.primary,
|
||||
validator: _model.textController1Validator
|
||||
.asValidator(context),
|
||||
),
|
||||
TextFormField(
|
||||
controller: _model.textController2,
|
||||
focusNode: _model.textFieldFocusNode2,
|
||||
autofocus: true,
|
||||
obscureText: false,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Channel ID',
|
||||
labelStyle: AppTheme.labelMedium,
|
||||
hintStyle: AppTheme.labelMedium,
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(
|
||||
color: AppTheme.alternate,
|
||||
width: 2,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(
|
||||
color: AppTheme.primary,
|
||||
width: 2,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(
|
||||
color: AppTheme.error,
|
||||
width: 2,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
focusedErrorBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(
|
||||
color: AppTheme.error,
|
||||
width: 2,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
contentPadding: const EdgeInsetsDirectional.fromSTEB(
|
||||
16, 12, 16, 12),
|
||||
),
|
||||
style: AppTheme.bodyMedium,
|
||||
cursorColor: AppTheme.primary,
|
||||
validator: _model.textController2Validator
|
||||
.asValidator(context),
|
||||
),
|
||||
TextFormField(
|
||||
controller: _model.textController3,
|
||||
focusNode: _model.textFieldFocusNode3,
|
||||
autofocus: true,
|
||||
obscureText: false,
|
||||
decoration: InputDecoration(
|
||||
labelStyle: AppTheme.labelMedium,
|
||||
hintText:
|
||||
'Short Description of what is going on...',
|
||||
hintStyle: AppTheme.labelMedium,
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(
|
||||
color: AppTheme.alternate,
|
||||
width: 2,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(
|
||||
color: AppTheme.primary,
|
||||
width: 2,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(
|
||||
color: AppTheme.error,
|
||||
width: 2,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
focusedErrorBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(
|
||||
color: AppTheme.error,
|
||||
width: 2,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
contentPadding: const EdgeInsetsDirectional.fromSTEB(
|
||||
16, 24, 16, 12),
|
||||
),
|
||||
style: AppTheme.bodyMedium,
|
||||
maxLines: 16,
|
||||
minLines: 6,
|
||||
cursorColor: AppTheme.primary,
|
||||
validator: _model.textController3Validator
|
||||
.asValidator(context),
|
||||
),
|
||||
].divide(const SizedBox(height: 12)),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 500,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
AppTheme.secondaryBackground,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: AppTheme.alternate,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.add_a_photo_rounded,
|
||||
color: AppTheme.primary,
|
||||
size: 32,
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
EdgeInsetsDirectional.fromSTEB(16, 0, 0, 0),
|
||||
child: Text(
|
||||
'Upload Screenshot',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
).animateOnPageLoad(
|
||||
animationsMap['containerOnPageLoadAnimation3']!),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsetsDirectional.fromSTEB(0, 24, 0, 12),
|
||||
child: FFButtonWidget(
|
||||
onPressed: () {
|
||||
print('Button pressed ...');
|
||||
},
|
||||
text: 'Submit Ticket',
|
||||
icon: const Icon(
|
||||
Icons.receipt_long,
|
||||
size: 15,
|
||||
),
|
||||
options: FFButtonOptions(
|
||||
width: double.infinity,
|
||||
height: 48,
|
||||
padding: const EdgeInsets.all(0),
|
||||
iconPadding:
|
||||
const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0),
|
||||
color: AppTheme.primary,
|
||||
textStyle: AppTheme.titleSmall,
|
||||
elevation: 4,
|
||||
borderSide: const BorderSide(
|
||||
color: Colors.transparent,
|
||||
width: 1,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(60),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user