import 'package:comunic/helpers/preferences_helper.dart'; import 'package:comunic/main.dart'; import 'package:comunic/models/config.dart'; import 'package:comunic/ui/routes/full_screen_image.dart'; import 'package:comunic/ui/widgets/dialogs/auto_sized_dialog_content_widget.dart'; import 'package:comunic/ui/widgets/dialogs/cancel_dialog_button.dart'; import 'package:comunic/utils/intl_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_emoji/flutter_emoji.dart'; import 'package:html/parser.dart'; /// User interface utilities /// /// @author Pierre HUBERT /// Build centered progress bar Widget buildCenteredProgressBar() { return Center( child: CircularProgressIndicator(), ); } /// Build and return a full loading page Widget buildLoadingPage({ bool showAppBar = false, String? routeTitle, }) { return Scaffold( appBar: showAppBar ? AppBar( title: routeTitle == null ? null : Text(routeTitle), ) : null, body: buildCenteredProgressBar(), ); } /// Build and return an error card Widget buildErrorCard(String? message, {List? actions, bool hide = false}) { if (hide) return Container(); return Theme( data: ThemeData( textTheme: TextTheme(bodyText2: TextStyle(color: Colors.white))), child: Card( elevation: 2.0, color: Colors.red, child: Padding( padding: const EdgeInsets.all(8.0), child: Row( children: [ Padding( padding: const EdgeInsets.only(right: 8.0), child: Icon( Icons.error, color: Colors.white, ), ), Flexible( child: Text( message!, maxLines: null, ), ), Row( children: actions == null ? [] : actions, ) ], ), ), ), ); } /// Show an image with a given [url] in full screen void showImageFullScreen(BuildContext context, String? url) { Navigator.of(context).push(MaterialPageRoute(builder: (c) { return FullScreenImageRoute(url!); })); } /// Show simple snack void showSimpleSnack(BuildContext context, String message) { ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message))); } void snack(BuildContext context, String message) { ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message))); } /// Show an alert dialog to ask the user to enter a string /// /// Returns entered string if the dialog is confirmed, null else Future askUserString({ required BuildContext context, required String title, required String message, required String defaultValue, required String hint, int maxLength = 200, int minLength = 1, }) async { assert(context != null); assert(title != null); assert(message != null); assert(defaultValue != null); assert(hint != null); assert(maxLength != null); TextEditingController controller = TextEditingController(text: defaultValue); final confirm = await showDialog( context: context, builder: (c) => _InputTextDialog( title: title, message: message, controller: controller, maxLength: maxLength, minLength: minLength, hint: hint, )); if (confirm == null || !confirm) return null; return controller.text; } class _InputTextDialog extends StatefulWidget { final String title; final String message; final TextEditingController controller; final int maxLength; final int minLength; final String hint; const _InputTextDialog({ Key? key, required this.title, required this.message, required this.controller, required this.maxLength, required this.minLength, required this.hint, }) : super(key: key); @override __InputTextDialogState createState() => __InputTextDialogState(); } class __InputTextDialogState extends State<_InputTextDialog> { @override Widget build(BuildContext c) => AlertDialog( title: Text(widget.title), content: AutoSizeDialogContentWidget( child: Column( children: [ Text(widget.message), TextField( controller: widget.controller, maxLines: null, maxLength: widget.maxLength, keyboardType: TextInputType.text, onChanged: (s) => setState(() {}), decoration: InputDecoration( labelText: widget.hint, alignLabelWithHint: true, ), ) ], ), ), actions: [ TextButton( child: Text(tr("Cancel")!.toUpperCase()), onPressed: () => Navigator.pop(c, false), ), TextButton( child: Text(tr("OK")!), onPressed: widget.controller.text.length >= widget.minLength ? () => Navigator.pop(c, true) : null, ), ], ); } /// Show an alert dialog to get user confirmation for something /// /// Return value of this function is never null Future showConfirmDialog({ required BuildContext context, String? title, required String? message, }) async { if (title == null) title = tr("Confirm operation"); // Avoid potential theme issues final scaffold = Scaffold.maybeOf(context); final ctx = scaffold != null ? scaffold.context : context; final result = await showDialog( context: ctx, builder: (c) => AlertDialog( title: Text(title!), content: Text(message!), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: Text(tr("Cancel")!.toUpperCase()), ), TextButton( onPressed: () => Navigator.pop(context, true), child: Text( tr("Confirm")!.toUpperCase(), style: TextStyle(color: Colors.red), ), ), ], )); return result != null && result; } /// Show a simple alert dialog Future showAlert({ required BuildContext context, required String? message, String? title, }) async => await showDialog( context: context, builder: (c) => AlertDialog( title: title == null ? null : Text(title), content: Text(message!), actions: [CancelDialogButton()], )); /// Smart [InputCounterWidgetBuilder] that show text limit only when some /// text has already been entered by the user Widget smartInputCounterWidgetBuilder( BuildContext context, { required int currentLength, required int? maxLength, required bool isFocused, }) => currentLength > 0 ? Text("$currentLength/$maxLength") : Container(); /// Parse an HTML String to decode special characters String htmlDecodeCharacters(String? input) { if (input == null || input == "") return ""; return parse(input).documentElement!.text; } const darkAccentColor = Colors.white70; const darkerAccentColor = Colors.white30; /// Check out whether dark theme is enabled or not bool darkTheme() => preferences()!.getBool(PreferencesKeyList.ENABLE_DARK_THEME); /// Check out whether we use tablet mode or not bool isTablet(BuildContext context) => !preferences()!.getBool(PreferencesKeyList.FORCE_MOBILE_MODE) && MediaQuery.of(context).size.width >= 1024; /// Show about Comunic dialog void showAboutAppDialog(BuildContext context) { showAboutDialog( context: context, applicationName: config().appName, children: [ Text( tr(config().appQuickDescription) ?? tr("Comunic is a free and OpenSource social network that respect your privacy.")!, textAlign: TextAlign.center, ), SizedBox( height: 20, ), Text( "Application built by Pierre Hubert", textAlign: TextAlign.center, ), ]); } /// Apply new theme settings void applyNewThemeSettings(BuildContext context) => context.findAncestorStateOfType()!.refresh(); /// Parse emojies String parseEmojies(String input) => EmojiParser().emojify(input); /// Create a white text style for dart heme and a black text otherwise TextStyle blackForWhiteTheme() => TextStyle(color: darkTheme() ? Colors.white : Colors.black);