dartlangeditorflutterflutter-appsflutter-examplesflutter-packageflutter-widgetquillquill-deltaquilljsreactquillrich-textrich-text-editorwysiwygwysiwyg-editor
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
202 lines
6.1 KiB
202 lines
6.1 KiB
4 years ago
|
import 'dart:async';
|
||
4 years ago
|
import 'dart:convert';
|
||
4 years ago
|
import 'dart:io';
|
||
4 years ago
|
|
||
4 years ago
|
import 'package:flutter/foundation.dart';
|
||
4 years ago
|
import 'package:flutter/material.dart';
|
||
4 years ago
|
import 'package:flutter/services.dart';
|
||
4 years ago
|
import 'package:flutter_quill/models/documents/attribute.dart';
|
||
4 years ago
|
import 'package:flutter_quill/models/documents/document.dart';
|
||
|
import 'package:flutter_quill/widgets/controller.dart';
|
||
4 years ago
|
import 'package:flutter_quill/widgets/default_styles.dart';
|
||
4 years ago
|
import 'package:flutter_quill/widgets/editor.dart';
|
||
4 years ago
|
import 'package:flutter_quill/widgets/toolbar.dart';
|
||
4 years ago
|
import 'package:path/path.dart';
|
||
4 years ago
|
import 'package:path_provider/path_provider.dart';
|
||
4 years ago
|
import 'package:tuple/tuple.dart';
|
||
|
|
||
|
import 'read_only_page.dart';
|
||
4 years ago
|
|
||
4 years ago
|
class HomePage extends StatefulWidget {
|
||
|
@override
|
||
|
_HomePageState createState() => _HomePageState();
|
||
|
}
|
||
|
|
||
|
class _HomePageState extends State<HomePage> {
|
||
4 years ago
|
QuillController? _controller;
|
||
4 years ago
|
final FocusNode _focusNode = FocusNode();
|
||
|
|
||
|
@override
|
||
|
void initState() {
|
||
|
super.initState();
|
||
|
_loadFromAssets();
|
||
|
}
|
||
|
|
||
|
Future<void> _loadFromAssets() async {
|
||
|
try {
|
||
4 years ago
|
final result = await rootBundle.loadString('assets/sample_data.json');
|
||
4 years ago
|
final doc = Document.fromJson(jsonDecode(result));
|
||
|
setState(() {
|
||
4 years ago
|
_controller = QuillController(
|
||
|
document: doc, selection: TextSelection.collapsed(offset: 0));
|
||
4 years ago
|
});
|
||
|
} catch (error) {
|
||
|
final doc = Document()..insert(0, 'Empty asset');
|
||
|
setState(() {
|
||
4 years ago
|
_controller = QuillController(
|
||
|
document: doc, selection: TextSelection.collapsed(offset: 0));
|
||
4 years ago
|
});
|
||
|
}
|
||
4 years ago
|
}
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
4 years ago
|
if (_controller == null) {
|
||
|
return Scaffold(body: Center(child: Text('Loading...')));
|
||
|
}
|
||
|
|
||
4 years ago
|
return Scaffold(
|
||
4 years ago
|
appBar: AppBar(
|
||
|
backgroundColor: Colors.grey.shade800,
|
||
|
elevation: 0,
|
||
|
centerTitle: false,
|
||
|
title: Text(
|
||
|
'Flutter Quill',
|
||
4 years ago
|
),
|
||
4 years ago
|
actions: [],
|
||
|
),
|
||
4 years ago
|
drawer: Container(
|
||
|
constraints:
|
||
|
BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.7),
|
||
4 years ago
|
color: Colors.grey.shade800,
|
||
|
child: _buildMenuBar(context),
|
||
|
),
|
||
4 years ago
|
body: RawKeyboardListener(
|
||
|
focusNode: FocusNode(),
|
||
|
onKey: (RawKeyEvent event) {
|
||
|
if (event.data.isControlPressed && event.character == 'b') {
|
||
4 years ago
|
if (_controller!
|
||
4 years ago
|
.getSelectionStyle()
|
||
|
.attributes
|
||
|
.keys
|
||
|
.contains("bold")) {
|
||
4 years ago
|
_controller!
|
||
4 years ago
|
.formatSelection(Attribute.clone(Attribute.bold, null));
|
||
|
} else {
|
||
4 years ago
|
_controller!.formatSelection(Attribute.bold);
|
||
4 years ago
|
print("not bold");
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
child: _buildWelcomeEditor(context),
|
||
|
),
|
||
4 years ago
|
);
|
||
4 years ago
|
}
|
||
|
|
||
|
Widget _buildWelcomeEditor(BuildContext context) {
|
||
4 years ago
|
return SafeArea(
|
||
4 years ago
|
child: Column(
|
||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
4 years ago
|
children: <Widget>[
|
||
4 years ago
|
Expanded(
|
||
4 years ago
|
flex: 15,
|
||
4 years ago
|
child: Container(
|
||
|
color: Colors.white,
|
||
|
padding: const EdgeInsets.only(left: 16.0, right: 16.0),
|
||
|
child: QuillEditor(
|
||
4 years ago
|
controller: _controller!,
|
||
4 years ago
|
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),
|
||
|
),
|
||
4 years ago
|
),
|
||
4 years ago
|
),
|
||
|
),
|
||
4 years ago
|
kIsWeb
|
||
|
? Expanded(
|
||
|
child: Container(
|
||
|
padding: EdgeInsets.symmetric(vertical: 16, horizontal: 8),
|
||
|
child: QuillToolbar.basic(
|
||
4 years ago
|
controller: _controller!,
|
||
4 years ago
|
onImagePickCallback: _onImagePickCallback),
|
||
|
))
|
||
|
: Container(
|
||
|
child: QuillToolbar.basic(
|
||
4 years ago
|
controller: _controller!,
|
||
4 years ago
|
onImagePickCallback: _onImagePickCallback),
|
||
|
),
|
||
4 years ago
|
],
|
||
|
),
|
||
4 years ago
|
);
|
||
|
}
|
||
4 years ago
|
|
||
4 years ago
|
// 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<String> _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();
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
Widget _buildMenuBar(BuildContext context) {
|
||
4 years ago
|
Size size = MediaQuery.of(context).size;
|
||
|
final itemStyle = TextStyle(
|
||
|
color: Colors.white,
|
||
|
fontSize: 18,
|
||
|
fontWeight: FontWeight.bold,
|
||
|
);
|
||
|
return Column(
|
||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||
4 years ago
|
children: [
|
||
4 years ago
|
Divider(
|
||
|
thickness: 2,
|
||
|
color: Colors.white,
|
||
|
indent: size.width * 0.1,
|
||
|
endIndent: size.width * 0.1,
|
||
|
),
|
||
4 years ago
|
ListTile(
|
||
4 years ago
|
title: Center(child: Text('Read only demo', style: itemStyle)),
|
||
4 years ago
|
dense: true,
|
||
|
visualDensity: VisualDensity.compact,
|
||
|
onTap: _readOnly,
|
||
4 years ago
|
),
|
||
|
Divider(
|
||
|
thickness: 2,
|
||
|
color: Colors.white,
|
||
|
indent: size.width * 0.1,
|
||
|
endIndent: size.width * 0.1,
|
||
|
),
|
||
4 years ago
|
],
|
||
|
);
|
||
|
}
|
||
|
|
||
|
void _readOnly() {
|
||
|
Navigator.push(
|
||
4 years ago
|
super.context,
|
||
4 years ago
|
MaterialPageRoute(
|
||
|
builder: (BuildContext context) => ReadOnlyPage(),
|
||
|
),
|
||
|
);
|
||
|
}
|
||
4 years ago
|
}
|