Flutter 使用 zefyr 富文本

4,560 阅读1分钟

这是 zefyr的git图片 下方是版本号,pub 也可以搜索 [last version]

zefyr: ^0.6.0 && image_picker: ^0.5.0

image_picker 不是最新版本,目前作者也没有更新(不过 issue有提)


例子:

import 'dart:async';
import 'dart:convert';
import 'package:image_picker/image_picker.dart';
import 'package:flutter/material.dart';
import 'package:quill_delta/quill_delta.dart';
import 'package:zefyr/zefyr.dart';
import '../../../services/service_method.dart'; // 个人的请求  (可以删掉)
import '../../../services/service_path.dart'; //  个人的请求的config (可以删掉)

class ZefyrLogo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Text('MDEditor'),
      ],
    );
  }
}

class UploadEdit extends StatefulWidget {
  @override
  _UploadEditState createState() => new _UploadEditState();
}

// 载入文本(渲染的话就可以这样,不渲染可以为空)
final doc = r'[{"insert":"Code blocks"},{"insert":"\n","attributes":{"heading":2}},{"insert":"Of course:\nimport ‘package:flutter/material.dart’;"},{"insert":"\n","attributes":{"block":"code"}},{"insert":"import ‘package:zefyr/zefyr.dart’;"},{"insert":"\n\n","attributes":{"block":"code"}},{"insert":"void main() {"},{"insert":"\n","attributes":{"block":"code"}},{"insert":" runApp(MyWAD());"},{"insert":"\n","attributes":{"block":"code"}},{"insert":"}"},{"insert":"\n","attributes":{"block":"code"}},{"insert":"\n嗯嗯\n"},{"insert":"​","attributes":{"embed":{"type":"image","source":"http://192.168.1.111:9000/static/articlePictre/GWBLPQHUIKmmexport1501334097933.jpg"}}},{"insert":"\n\n"}]';

Delta getDelta() {
  return Delta.fromJson(json.decode(doc) as List);
}

class _UploadEditState extends State<UploadEdit> {
  final ZefyrController _controller =
      ZefyrController(NotusDocument.fromDelta(getDelta()));
  final FocusNode _focusNode = new FocusNode();
  bool _editing = false;
  StreamSubscription<NotusChange> _sub;

  @override
  void initState() {
    super.initState();
    _sub = _controller.document.changes.listen((change) {
    // 改变的行数,及其内容
      print('change.source is ${change.source}:\n change.change is  ${change.change}');
      // 转换成 delta(一般转换成这样处理)
      print("Delta() is>>>>>>>>>>>>>>>>>>>>. ${_controller.document.toDelta()} \n");
      // 转换成 Json
      print("Json() is>>>>>>>>>>>>>>>>>>>>. ${json.encode(_controller.document.toJson()) } \n");
      // 转换成 string
      print("String() is>>>>>>>>>>>>>>>>>>>>. ${_controller.document.toString()} \n");
    });
  }

  @override
  void dispose() {
    // 释放
    _sub.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // 改变样式   
    final theme = new ZefyrThemeData(
      cursorColor: Colors.blue,
      toolbarTheme: ZefyrToolbarTheme.fallback(context).copyWith(
        color: Colors.grey.shade800,
        toggleColor: Colors.grey.shade900,
        iconColor: Colors.white,
        disabledIconColor: Colors.grey.shade500,
      ),
    );

    final done = _editing
        ? [new FlatButton(onPressed: _stopEditing, child: Text('DONE'))]
        : [new FlatButton(onPressed: _startEditing, child: Text('EDIT'))];
        
    return Scaffold(
      resizeToAvoidBottomPadding: true,
      appBar: AppBar(
        elevation: 1.0,
        backgroundColor: Colors.grey.shade200,
        brightness: Brightness.light,
        title: ZefyrLogo(),
        actions: done,
      ),
      body: ZefyrScaffold(
        child: ZefyrTheme(
          data: theme,
          child: ZefyrEditor(
            controller: _controller,
            focusNode: _focusNode,
            enabled: _editing,
            imageDelegate: new CustomImageDelegate(),
          ),
        ),
      ),
    );
  }

  void _startEditing() {
    setState(() {
      _editing = true;
    });
  }

  void _stopEditing() {
    setState(() {
      _editing = false;
    });
  }
}

/// Custom image delegate used by this example to load image from application
/// assets.
///
/// Default image delegate only supports [FileImage]s.
class CustomImageDelegate extends ZefyrDefaultImageDelegate {
  String url = "";
  
  @override
  Future<String> pickImage(ImageSource source) async {
    final file = await ImagePicker.pickImage(source: source);
    if (file == null) return null;
    // Use my storage service to upload selected file. The uploadImage method
    // returns unique ID of newly uploaded image on my server.
    
    // 这里是我的后端地址(你们可以写自己的后台地址
    var request = await requestfile("url",file, null);
    print(request);
    if(request["code"] == "200") {
      print("图片上传成功");
    } else {
      print("图片上传失败");
    }
      url = "$SERVER_URL/static/articlePictre/${request["data"]}";
    return url;
  }

  @override
  Widget buildImage(BuildContext context, String imageSource) {
    // We use custom "asset" scheme to distinguish asset images from other files.
    print("imageSource is $imageSource");
    // 这里拿到刚才return url 也就是 delta 的文本内容,不可以使用上面的url,直接 imageSource 就可以
    return Image.network(imageSource);
  }
}

偷懒也没有重构 model,class 类来写,不过基本内容可以满足,视频的话,要单独处理,这个package没有提供这样的内容。