import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_quill/models/documents/attribute.dart'; import 'package:flutter_quill/models/documents/document.dart'; import 'package:flutter_quill/widgets/controller.dart'; import 'package:flutter_quill/widgets/default_styles.dart'; import 'package:flutter_quill/widgets/editor.dart'; import 'package:flutter_quill/widgets/toolbar.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; import 'package:tuple/tuple.dart'; import 'read_only_page.dart'; class HomePage extends StatefulWidget { @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State { QuillController? _controller; final FocusNode _focusNode = FocusNode(); @override void initState() { super.initState(); _loadFromAssets(); } Future _loadFromAssets() async { try { final result = await rootBundle.loadString('assets/sample_data.json'); final doc = Document.fromJson(jsonDecode(result)); setState(() { _controller = QuillController( document: doc, selection: TextSelection.collapsed(offset: 0)); }); } catch (error) { final doc = Document()..insert(0, 'Empty asset'); setState(() { _controller = QuillController( document: doc, selection: TextSelection.collapsed(offset: 0)); }); } } @override Widget build(BuildContext context) { if (_controller == null) { return Scaffold(body: Center(child: Text('Loading...'))); } return Scaffold( appBar: AppBar( backgroundColor: Colors.grey.shade800, elevation: 0, centerTitle: false, title: Text( 'Flutter Quill', ), actions: [], ), drawer: Container( constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.7), color: Colors.grey.shade800, child: _buildMenuBar(context), ), body: RawKeyboardListener( focusNode: FocusNode(), onKey: (RawKeyEvent event) { if (event.data.isControlPressed && event.character == 'b') { if (_controller! .getSelectionStyle() .attributes .keys .contains("bold")) { _controller! .formatSelection(Attribute.clone(Attribute.bold, null)); } else { _controller!.formatSelection(Attribute.bold); print("not bold"); } } }, child: _buildWelcomeEditor(context), ), ); } Widget _buildWelcomeEditor(BuildContext context) { return SafeArea( child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( flex: 15, child: Container( color: Colors.white, padding: const EdgeInsets.only(left: 16.0, right: 16.0), child: QuillEditor( controller: _controller!, scrollController: ScrollController(), scrollable: true, focusNode: _focusNode, autoFocus: false, readOnly: false, placeholder: 'Add content', enableInteractiveSelection: true, expands: false, padding: EdgeInsets.zero, customStyles: DefaultStyles( h1: DefaultTextBlockStyle( TextStyle( fontSize: 32.0, color: Colors.black, height: 1.15, fontWeight: FontWeight.w300, ), Tuple2(16.0, 0.0), Tuple2(0.0, 0.0), null), sizeSmall: TextStyle(fontSize: 9.0), ), ), ), ), kIsWeb ? Expanded( child: Container( padding: EdgeInsets.symmetric(vertical: 16, horizontal: 8), child: QuillToolbar.basic( controller: _controller!, onImagePickCallback: _onImagePickCallback), )) : Container( child: QuillToolbar.basic( controller: _controller!, onImagePickCallback: _onImagePickCallback), ), ], ), ); } // Renders the image picked by imagePicker from local file storage // You can also upload the picked image to any server (eg : AWS s3 or Firebase) and then return the uploaded image URL Future _onImagePickCallback(File file) async { // Copies the picked file from temporary cache to applications directory Directory appDocDir = await getApplicationDocumentsDirectory(); File copiedFile = await file.copy('${appDocDir.path}/${basename(file.path)}'); return copiedFile.path.toString(); } Widget _buildMenuBar(BuildContext context) { Size size = MediaQuery.of(context).size; final itemStyle = TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold, ); return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Divider( thickness: 2, color: Colors.white, indent: size.width * 0.1, endIndent: size.width * 0.1, ), ListTile( title: Center(child: Text('Read only demo', style: itemStyle)), dense: true, visualDensity: VisualDensity.compact, onTap: _readOnly, ), Divider( thickness: 2, color: Colors.white, indent: size.width * 0.1, endIndent: size.width * 0.1, ), ], ); } void _readOnly() { Navigator.push( super.context, MaterialPageRoute( builder: (BuildContext context) => ReadOnlyPage(), ), ); } }