import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:html_editor_enhanced/html_editor.dart'; import 'package:file_picker/file_picker.dart'; void main() => runApp(HtmlEditorExampleApp()); class HtmlEditorExampleApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData(), darkTheme: ThemeData.dark(), home: HtmlEditorExample(title: 'Flutter HTML Editor Example'), ); } } class HtmlEditorExample extends StatefulWidget { HtmlEditorExample({Key? key, required this.title}) : super(key: key); final String title; @override _HtmlEditorExampleState createState() => _HtmlEditorExampleState(); } class _HtmlEditorExampleState extends State { String result = ''; final HtmlEditorController controller = HtmlEditorController(); @override Widget build(BuildContext context) { return GestureDetector( onTap: () { if (!kIsWeb) { controller.clearFocus(); } }, child: Scaffold( appBar: AppBar( title: Text(widget.title), elevation: 0, actions: [ IconButton( icon: Icon(Icons.refresh), onPressed: () { if (kIsWeb) { controller.reloadWeb(); } else { controller.editorController!.reload(); } }) ], ), floatingActionButton: FloatingActionButton( onPressed: () { controller.toggleCodeView(); }, child: Text(r'<\>', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)), ), body: SingleChildScrollView( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ HtmlEditor( controller: controller, htmlEditorOptions: HtmlEditorOptions( hint: 'Your text here...', shouldEnsureVisible: true, //initialText: "

text content initial, if any

", ), htmlToolbarOptions: HtmlToolbarOptions( toolbarPosition: ToolbarPosition.aboveEditor, //by default toolbarType: ToolbarType.nativeScrollable, //by default onButtonPressed: (ButtonType type, bool? status, Function? updateStatus) { print( "button '${describeEnum(type)}' pressed, the current selected status is $status"); return true; }, onDropdownChanged: (DropdownType type, dynamic changed, Function(dynamic)? updateSelectedItem) { print( "dropdown '${describeEnum(type)}' changed to $changed"); return true; }, mediaLinkInsertInterceptor: (String url, InsertFileType type) { print(url); return true; }, mediaUploadInterceptor: (PlatformFile file, InsertFileType type) async { print(file.name); //filename print(file.size); //size in bytes print(file.extension); //file extension (eg jpeg or mp4) return true; }, ), otherOptions: OtherOptions(height: 550), callbacks: Callbacks(onBeforeCommand: (String? currentHtml) { print('html before change is $currentHtml'); }, onChangeContent: (String? changed) { print('content changed to $changed'); }, onChangeCodeview: (String? changed) { print('code changed to $changed'); }, onChangeSelection: (EditorSettings settings) { print('parent element is ${settings.parentElement}'); print('font name is ${settings.fontName}'); }, onDialogShown: () { print('dialog shown'); }, onEnter: () { print('enter/return pressed'); }, onFocus: () { print('editor focused'); }, onBlur: () { print('editor unfocused'); }, onBlurCodeview: () { print('codeview either focused or unfocused'); }, onInit: () { print('init'); }, //this is commented because it overrides the default Summernote handlers /*onImageLinkInsert: (String? url) { print(url ?? "unknown url"); }, onImageUpload: (FileUpload file) async { print(file.name); print(file.size); print(file.type); print(file.base64); },*/ onImageUploadError: (FileUpload? file, String? base64Str, UploadError error) { print(describeEnum(error)); print(base64Str ?? ''); if (file != null) { print(file.name); print(file.size); print(file.type); } }, onKeyDown: (int? keyCode) { print('$keyCode key downed'); print( 'current character count: ${controller.characterCount}'); }, onKeyUp: (int? keyCode) { print('$keyCode key released'); }, onMouseDown: () { print('mouse downed'); }, onMouseUp: () { print('mouse released'); }, onNavigationRequestMobile: (String url) { print(url); return NavigationActionPolicy.ALLOW; }, onPaste: () { print('pasted into editor'); }, onScroll: () { print('editor scrolled'); }), plugins: [ SummernoteAtMention( getSuggestionsMobile: (String value) { var mentions = ['test1', 'test2', 'test3']; return mentions .where((element) => element.contains(value)) .toList(); }, mentionsWeb: ['test1', 'test2', 'test3'], onSelect: (String value) { print(value); }), ], ), Padding( padding: const EdgeInsets.all(8.0), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( style: TextButton.styleFrom( backgroundColor: Colors.blueGrey), onPressed: () { controller.undo(); }, child: Text('Undo', style: TextStyle(color: Colors.white)), ), SizedBox( width: 16, ), TextButton( style: TextButton.styleFrom( backgroundColor: Colors.blueGrey), onPressed: () { controller.clear(); }, child: Text('Reset', style: TextStyle(color: Colors.white)), ), SizedBox( width: 16, ), TextButton( style: TextButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.secondary), onPressed: () async { var txt = await controller.getText(); if (txt.contains('src=\"data:')) { txt = ''; } setState(() { result = txt; }); }, child: Text( 'Submit', style: TextStyle(color: Colors.white), ), ), SizedBox( width: 16, ), TextButton( style: TextButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.secondary), onPressed: () { controller.redo(); }, child: Text( 'Redo', style: TextStyle(color: Colors.white), ), ), ], ), ), Padding( padding: const EdgeInsets.all(8.0), child: Text(result), ), Padding( padding: const EdgeInsets.all(8.0), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( style: TextButton.styleFrom( backgroundColor: Colors.blueGrey), onPressed: () { controller.disable(); }, child: Text('Disable', style: TextStyle(color: Colors.white)), ), SizedBox( width: 16, ), TextButton( style: TextButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.secondary), onPressed: () async { controller.enable(); }, child: Text( 'Enable', style: TextStyle(color: Colors.white), ), ), ], ), ), SizedBox(height: 16), Padding( padding: const EdgeInsets.all(8.0), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( style: TextButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.secondary), onPressed: () { controller.insertText('Google'); }, child: Text('Insert Text', style: TextStyle(color: Colors.white)), ), SizedBox( width: 16, ), TextButton( style: TextButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.secondary), onPressed: () { controller.insertHtml( '''

Google in blue

'''); }, child: Text('Insert HTML', style: TextStyle(color: Colors.white)), ), ], ), ), Padding( padding: const EdgeInsets.all(8.0), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( style: TextButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.secondary), onPressed: () async { controller.insertLink( 'Google linked', 'https://google.com', true); }, child: Text( 'Insert Link', style: TextStyle(color: Colors.white), ), ), SizedBox( width: 16, ), TextButton( style: TextButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.secondary), onPressed: () { controller.insertNetworkImage( 'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png', filename: 'Google network image'); }, child: Text( 'Insert network image', style: TextStyle(color: Colors.white), ), ), ], ), ), SizedBox(height: 16), Padding( padding: const EdgeInsets.all(8.0), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( style: TextButton.styleFrom( backgroundColor: Colors.blueGrey), onPressed: () { controller.addNotification( 'Info notification', NotificationType.info); }, child: Text('Info', style: TextStyle(color: Colors.white)), ), SizedBox( width: 16, ), TextButton( style: TextButton.styleFrom( backgroundColor: Colors.blueGrey), onPressed: () { controller.addNotification( 'Warning notification', NotificationType.warning); }, child: Text('Warning', style: TextStyle(color: Colors.white)), ), SizedBox( width: 16, ), TextButton( style: TextButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.secondary), onPressed: () async { controller.addNotification( 'Success notification', NotificationType.success); }, child: Text( 'Success', style: TextStyle(color: Colors.white), ), ), SizedBox( width: 16, ), TextButton( style: TextButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.secondary), onPressed: () { controller.addNotification( 'Danger notification', NotificationType.danger); }, child: Text( 'Danger', style: TextStyle(color: Colors.white), ), ), ], ), ), SizedBox(height: 16), Padding( padding: const EdgeInsets.all(8.0), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( style: TextButton.styleFrom( backgroundColor: Colors.blueGrey), onPressed: () { controller.addNotification('Plaintext notification', NotificationType.plaintext); }, child: Text('Plaintext', style: TextStyle(color: Colors.white)), ), SizedBox( width: 16, ), TextButton( style: TextButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.secondary), onPressed: () async { controller.removeNotification(); }, child: Text( 'Remove', style: TextStyle(color: Colors.white), ), ), ], ), ), ], ), ), ), ); } }