0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[flutter]Quill(rich text editor)で端末から画像アップロードする方法

Posted at

quillエディタで端末から画像アップロードする方法をお教えします。

結論から言うとquillでは画像のリンクから画像アップロードすることはできますが、端末からアップロードする方法はなく自作するしかありません(少なくともドキュメントはない)

※前提として状態管理はriverpodを使用しています。

Quillエディタ公式ドキュメント

公式ドキュメントはこちら
ライブラリのインポート等は公式ドキュメントを参照してください。
ここでは省略します。

カスタムボタンの追加

カスタムボタンの追加
QuillToolbar.basic(
    customButtons: [
                  QuillCustomButton(
                      icon:Icons.camera_alt,
                      onTap: () {
                        getImageFromGallery(ref);
                        ///ここで端末のフォルダを参照している

                      }
                  ),

                ],
           )

上記のようにquillのツールバーにはカスタムボタンを作れるのでますは任意のアイコンと押下時のロジックを用意しておきましょう。

imagePickerを追加

公式ドキュメントはこちら

ライブラリのインポート等は公式ドキュメントを参照してください。
ここでは省略します。

実装

カスタムボタンの追加
Future getImageFromGallery(WidgetRef ref) async {
  final pickedFile = await picker.pickImage(source: ImageSource.gallery,maxHeight: 380, maxWidth: 340);//デフォルトサイズは一旦こんな感じ

    if(pickedFile != null) {
      ref.watch(imageProvider.notifier).update((state) =>  File(pickedFile.path));
    }
  final index = _controller.selection.baseOffset;
  final length = _controller.selection.extentOffset - index;

  print(File(pickedFile!.path));

  _controller.replaceText(index, length, BlockEmbed.image(File(pickedFile.path).path), null);
 // BlockEmbed.image(value)
}

全コード

サンプルコード
QuillController _controller = QuillController.basic();


class CommentWidget extends StatelessWidget {
  const CommentWidget({
    super.key,
  });

  @override
  Widget build(BuildContext context) {


    return Scaffold(
      body: Column(
        children: [
          SingleChildScrollView(
            child: Container(
              height: 56,
              decoration: const BoxDecoration(
                color: whiteColor,
                borderRadius: BorderRadius.only(
                  topLeft: Radius.circular(12.0),
                  topRight: Radius.circular(12.0),
                ),
              ),
              child: Stack(
                children: [
                  ConstrainedBox(
                    constraints: const BoxConstraints.expand(),
                    child: Row(
                        mainAxisAlignment: MainAxisAlignment.end,
                        children: [
                          IconButton(
                            icon: Transform.rotate(
                                angle: 45 * pi / 180,
                                child: const Icon(Icons.add,
                                    size: 45, color: blackColor)),
                            onPressed: () {
                              Navigator.pop(context);
                            },
                          ),
                          const SizedBox(
                            width: 6,
                          )
                        ]),
                  ),
                  // const Center(
                  //   child: Text('ボトムシート',
                  //       style: TextStyle(
                  //           fontWeight: FontWeight.bold,
                  //           color: Colors.white,
                  //           fontSize: 16)),
                  // )
                ],
              ),
            ),
          ),

          Consumer(

            builder: (BuildContext context, WidgetRef ref, Widget? child) {

              return QuillToolbar.basic(
           
                toolbarSectionSpacing: 0,
                toolbarIconCrossAlignment: WrapCrossAlignment.end,
                toolbarIconAlignment: WrapAlignment.start,
                color: whiteColor,
                toolbarIconSize: 20,
           

                customButtons: [
                  QuillCustomButton(
                      icon:Icons.camera_alt,
                      onTap: () {
                        getImageFromGallery(ref);
                        ///ここで画像取得できるかやってみる

                      }
                  ),

                ],
                controller: _controller,

                iconTheme: const QuillIconTheme(
                    borderRadius: 14,
                    iconSelectedFillColor: basedAccentColor
                ),
              );
            },

          ),

         // const SizedBox(height: 20,),

          Expanded(
            child: SingleChildScrollView(
              child: Column(
                children: [
                  Padding(
                    padding: const EdgeInsets.only(left: 10,bottom: 10,right: 10),
                    child: Container(

                      height: 300,
                      color: whiteColor,
                      child: QuillEditor(


                        embedBuilders: FlutterQuillEmbeds.builders(),
                        keyboardAppearance: Brightness.dark,
                        controller: _controller,
                        placeholder: 'タイトル',
                        readOnly: false,
                        focusNode: FocusNode(),
                        scrollController: ScrollController(),
                        scrollable: true,
                        padding: EdgeInsets.zero,
                        autoFocus: true,
                        expands: false,
                        // true for view only mode
                      ),
                    ),
                  ),


                  Padding(
                    padding: const EdgeInsets.only(left: 10,bottom: 10,right: 10),
                    child: Container(

                      height: 300,
                      color: whiteColor,
                      child: QuillEditor(


                        embedBuilders: FlutterQuillEmbeds.builders(),
                        keyboardAppearance: Brightness.dark,
                        controller: _controller2,
                        placeholder: 'みんなのためになる事を書きましょう。',
                        readOnly: false,
                        focusNode: FocusNode(),
                        scrollController: ScrollController(),
                        scrollable: true,
                        padding: EdgeInsets.zero,
                        autoFocus: true,
                        expands: false,
                        // true for view only mode
                      ),
                    ),
                  ),


                  Padding(
                    padding: const EdgeInsets.only(left: 10,bottom: 10,right: 10),
                    child: Container(

                      height: 300,
                      color: whiteColor,
                      child: QuillEditor(


                        embedBuilders: FlutterQuillEmbeds.builders(),
                        keyboardAppearance: Brightness.dark,
                        controller: _controller3,
                        placeholder: 'みんなのためになる事を書きましょう。',
                        readOnly: false,
                        focusNode: FocusNode(),
                        scrollController: ScrollController(),
                        scrollable: true,
                        padding: EdgeInsets.zero,
                        autoFocus: true,
                        expands: false,
                        // true for view only mode
                      ),
                    ),
                  ),


                ],
              ),
            ),
          ),





       //   const SizedBox(height: 30,),
          Padding(
            padding: const EdgeInsets.only(bottom: 10),
            child: SizedBox(

              width: 320,
              height: 40,
              child: DefaultButtonWidget(text: '投稿', width: 340,mainColor: basedAccentColor,textColor: Colors.white,
                  onPressed: () {
                 print(_controller.document.toDelta().toJson());



                 print('_controller.document.length${_controller.document.length}');

                //  final index = _controller.selection.baseOffset;
                //  final length = _controller.selection.extentOffset - index;
                // print(index);
                // print(length);
                 for(int i = 0;i<_controller.document.length; i++) {
                   print(_controller.document.length);
                   if (_controller.hasUndo) {
                     _controller.clear();
                   }
                   else {
                     print('for break');
                     break;
                   }
                 }




                Navigator.pop(context);

                // Navigator.push(
                //   context,
                //   MaterialPageRoute(builder: (context) => const UserReportView()),
                // );
              })
            ),
          ),
        ],
      ),
    );
  }
}


final imageProvider = StateProvider<File?>((ref) => null);
//画像を入れる変数
final picker = ImagePicker(); //Image Pickerをインスタンス化


Future getImageFromGallery(WidgetRef ref) async {
  final pickedFile = await picker.pickImage(source: ImageSource.gallery,maxHeight: 380, maxWidth: 340);//デフォルトサイズは一旦こんな感じ

    if(pickedFile != null) {
      ref.watch(imageProvider.notifier).update((state) =>  File(pickedFile.path));
    }
  final index = _controller.selection.baseOffset;
  final length = _controller.selection.extentOffset - index;

  print(File(pickedFile!.path));

  _controller.replaceText(index, length, BlockEmbed.image(File(pickedFile.path).path), null);
 // BlockEmbed.image(value)
}

最後に

リッチテキストエディタ要件を全て満たそうとすると途端に何度上がる印象です。。
もっといいライブラリがあればいいのですが、、

もし上記の記事に対するご指摘などあればぜひよろしくお願いします!!

最後まで読んでいただきありがとうございました。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?