import 'dart:async'; import 'dart:convert'; import 'dart:typed_data'; import 'package:badges/badges.dart' as badges; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:image_picker/image_picker.dart'; import 'package:loader_overlay/loader_overlay.dart'; import 'package:package_info_plus/package_info_plus.dart'; import '../../../../api/mhsl/server/feedback/add_feedback.dart'; import '../../../../api/mhsl/server/feedback/add_feedback_params.dart'; import '../../../../model/account_data.dart'; import '../../../../state/app/modules/settings/bloc/settings_cubit.dart'; import '../../../../widget/file_pick.dart'; import '../../../../widget/focus_behaviour.dart'; import '../../../../widget/info_dialog.dart'; class FeedbackDialog extends StatefulWidget { const FeedbackDialog({super.key}); @override State createState() => _FeedbackDialogState(); } class _FeedbackDialogState extends State { final ImagePicker picker = ImagePicker(); final TextEditingController _feedbackInput = TextEditingController(); Uint8List? _image; String? _error; bool _textFieldEmpty = false; @override void initState() { super.initState(); _feedbackInput.addListener(() { setState(() { _textFieldEmpty = _feedbackInput.text.isEmpty; _error = null; }); }); } @override Widget build(BuildContext context) => Scaffold( appBar: AppBar(title: const Text('Feedback')), body: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.max, children: [ const SizedBox(height: 5), const Text( 'Feedback, Anregungen, Ideen, Fehler und Verbesserungen', textAlign: TextAlign.center, ), const SizedBox(height: 15), const Text( 'Bitte gib keine geheimen Daten wie z.B. Passwörter weiter.', textAlign: TextAlign.center, style: TextStyle(fontSize: 11), ), const SizedBox(height: 20), Padding( padding: const EdgeInsets.all(10), child: TextField( onChanged: (value) { if (value.trim().toLowerCase() == 'ranzig') { _feedbackInput.text = 'selber'; } }, controller: _feedbackInput, autofocus: true, decoration: InputDecoration( border: const OutlineInputBorder(), label: const Text('Feedback und Verbesserungen'), errorText: _textFieldEmpty ? 'Bitte gib eine Beschreibung an!' : null, ), minLines: 4, maxLines: 7, onTapOutside: (PointerDownEvent event) => FocusBehaviour.textFieldTapOutside(context), ), ), const SizedBox(height: 10), if (_image != null) Row( mainAxisAlignment: MainAxisAlignment.center, children: [ badges.Badge( badgeContent: const Icon(Icons.close_outlined, size: 17), badgeStyle: const badges.BadgeStyle( padding: EdgeInsets.all(2), ), child: Container( decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(5)), border: Border.all( width: 3, color: Theme.of(context).primaryColor, ), ), height: 150, child: Image( image: Image.memory(_image!).image, fit: BoxFit.contain, ), ), onTap: () async { setState(() { _image = null; }); }, ), ], ), Padding( padding: const EdgeInsets.all(5), child: Visibility( visible: _error != null, child: Visibility( visible: context.read().val().devToolsEnabled, replacement: const Text( 'Senden fehlgeschlagen, bitte überprüfe die Internetverbindung.', textAlign: TextAlign.center, style: TextStyle(color: Colors.red), ), child: Text( 'Senden fehlgeschlagen: \n $_error', textAlign: TextAlign.center, style: const TextStyle(color: Colors.red), ), ), ), ), Padding( padding: const EdgeInsets.only(right: 20, left: 10), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Visibility( visible: _image == null, child: IconButton( onPressed: () async { context.loaderOverlay.show(); final picked = await FilePick.multipleGalleryPick(); final imageData = await picked?.first.readAsBytes(); if (context.mounted) context.loaderOverlay.hide(); setState(() { _image = imageData; }); }, icon: const Icon(Icons.attach_file_outlined), ), ), const Expanded(child: SizedBox.shrink()), TextButton( onPressed: () async { if (_feedbackInput.text.isEmpty) { setState(() { _textFieldEmpty = true; }); return; } context.loaderOverlay.show(); unawaited( AddFeedback( AddFeedbackParams( user: AccountData().getUserSecret(), feedback: _feedbackInput.text, screenshot: _image != null ? base64Encode(_image!) : null, appVersion: int.parse( (await PackageInfo.fromPlatform()).buildNumber, ), ), ) .run() .then((value) { if (!context.mounted) return; Navigator.of(context).pop(); InfoDialog.show( context, 'Danke für dein Feedback!', ); context.loaderOverlay.hide(); }) .catchError((Object error, StackTrace trace) { if (!mounted) return; setState(() { _error = error.toString(); }); if (!context.mounted) return; context.loaderOverlay.hide(); }), ); }, child: const Text('Senden'), ), ], ), ), ], ), ), ); }