tneotia / html-editor-enhanced

A Flutter package that provides a WYSIWYG editor backed by flutter_inappwebview and the Summernote library.
https://pub.dev/packages/html_editor_enhanced
MIT License
275 stars 336 forks source link

how can I add a dropdown with custom font style choices for editor text #59

Closed Ashish-Raturi closed 3 years ago

Ashish-Raturi commented 3 years ago

Hi, you guys are great, I have a question, how can I add my custom fonts on text editor and custom text color, or custom color widget thanks in advance.

tneotia commented 3 years ago

If you want custom toolbar buttons then simply do this:

HtmlEditor(
  controller: controller,
  htmlToolbarOptions: HtmlToolbarOptions(
    defaultToolbarButtons: [
      StyleButtons(),
      FontSettingButtons(fontName: false),
      FontButtons(),
      ListButtons(),
      ParagraphButtons(),
      InsertButtons(),
      OtherButtons(),
    ],
    customToolbarButtons: [
      CustomFont(),
      CustomTextColor(),
    ],
  ),
),

defaultToolbarButtons is there to disable the default font dropdown and default color pickers. Then you use customToolbarButtons to add in your own widgets however you want to.

For implementing a custom font picker or custom color picker I highly recommend checking out how I did the default version in toolbar_widget.dart at lines 500 and 905.

Now that I think about it, there is no way for custom implementations to be updated when the font family/color/font size, etc changes in the editor. Internally I handle this with a method listening to changes sent in JSON, but I think its a good idea to add some sort of callback/listener/stream to the package so custom UI elements can be updated. Therefore I am adding the "enhancement" tag to this issue.

Let me know if you need more help with the implementation

Ashish-Raturi commented 3 years ago

hi, thanks for your reply, I added custom drop down menu item, it's showing only the font name but not changing it on editor. or maybe i'm doing it wrong. can you tell me how to add it, https://user-images.githubusercontent.com/50191855/121620011-36940c80-ca87-11eb-809f-814f324cc248.mp4

I added them here

  items: [
              CustomDropdownMenuItem(
                value: 'Courier New',
                child: PointerInterceptor(
                    child: Text('Courier New',
                        style: TextStyle(fontFamily: 'Courier'))),
              ),
              CustomDropdownMenuItem(
                value: 'sans-serif',
                child: PointerInterceptor(
                    child: Text('Sans Serif',
                        style: TextStyle(fontFamily: 'sans-serif'))),
              ),
              CustomDropdownMenuItem(
                value: 'Times New Roman',
                child: PointerInterceptor(
                    child: Text('Times New Roman',
                        style: TextStyle(fontFamily: 'Times'))),
              ),
              CustomDropdownMenuItem(
                value: 'Billabong',
                child: PointerInterceptor(
                    child: Text('Billabong',
                        style: TextStyle(
                          fontFamily: 'Billabong',
                        ))),
              ),
              CustomDropdownMenuItem(
                value: 'AlexBrush',
                child: PointerInterceptor(
                    child: Text('AlexBrush',
                        style: TextStyle(
                          fontFamily: 'AlexBrush',
                        ))),
              ),
              CustomDropdownMenuItem(
                value: 'Allura',
                child: PointerInterceptor(
                    child: Text('Allura',
                        style: TextStyle(
                          fontFamily: 'Allura',
                        ))),
              ),
              CustomDropdownMenuItem(
                value: 'Arizonia',
                child: PointerInterceptor(
                    child: Text('Arizonia',
                        style: TextStyle(
                          fontFamily: 'Arizonia',
                        ))),
              ),
            ],
            value: _fontNameSelectedItem,
            onChanged: (String? changed) async {
              void updateSelectedItem(dynamic changed) async {
                if (changed is String) {
                  setState(mounted, this.setState, () {
                    _fontNameSelectedItem = changed;
                  });
                }
              }

              if (changed != null) {
                var proceed =
                    await widget.htmlToolbarOptions.onDropdownChanged?.call(
                            DropdownType.fontName,
                            changed,
                            updateSelectedItem) ??
                        true;
                if (proceed) {
                  widget.controller
                      .execCommand('fontName', argument: changed);
                  updateSelectedItem(changed);
                }
              }
            },
          ),

and here

   //check the font name if it matches one of the predetermined fonts and update the toolbar
  if ([
    'Courier New',
    'sans-serif',
    'Times New Roman',
    'Billabong',
    'AlexBrush'
  ].contains(fontName)) {
    setState(mounted, this.setState, () {
      _fontNameSelectedItem = fontName;
    });
  } else {
    setState(mounted, this.setState, () {
      _fontNameSelectedItem = 'sans-serif';
    });
  }

pubspec.yaml

  fonts:
    - family: AlexBrush
      fonts:
        - asset: assets/fontFamily/AlexBrush-Regular.ttf
    - family: Allura
      fonts:
        - asset: assets/fontFamily/Allura-Regular.ttf
    - family: Arizonia
      fonts:
        - asset: assets/fontFamily/Arizonia-Regular.ttf
    - family: ChunkFive
      fonts:
        - asset: assets/fontFamily/Chunk_Five_Print.ttf
    - family: GrandHotel
      fonts:
        - asset: assets/fontFamily/GrandHotel-Regular.otf
    - family: GreatVibes
      fonts:
        - asset: assets/fontFamily/GreatVibes-Regular.ttf
    - family: Lobster
      fonts:
        - asset: assets/fontFamily/Lobster-Regular.ttf
    - family: OpenSans
      fonts:
        - asset: assets/fontFamily/OpenSans-Regular.ttf
        - asset: assets/fontFamily/OpenSans-Italic.ttf
          style: italic
        - asset: assets/fontFamily/OpenSans-Bold.ttf
          weight: 700

later I tried to add a new custom button also and call this controller.execCommand('fontName', argument: 'AlexBrush'); this just changing it on drop down, not change text style

Ashish-Raturi commented 3 years ago

and one more thing, on real device, it not showing the editor, tool are showing and it's working perfectly in emulator did need to add any kind for permission of something else. please help

tneotia commented 3 years ago

2 things you are doing wrong I think:

  1. You need to add the fonts into the editor itself because webview fonts are different from Flutter fonts, you need to make a custom HTML file like this:

    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <meta name="description" content="Flutter Summernote HTML Editor">
    <meta name="author" content="tneotia">
    <title>Summernote Text Editor HTML</title>
    <script src="assets/packages/html_editor_enhanced/assets/jquery.min.js"></script>
    <link href="assets/packages/html_editor_enhanced/assets/summernote-lite.min.css" rel="stylesheet">
    <script src="assets/packages/html_editor_enhanced/assets/summernote-lite.min.js"></script>
    <style>
    <!--your font faces go here-->
    </style>
    <!--darkCSS-->
    </head>
    <body>
    <div id="summernote-2"></div>
    <!--headString-->
    <!--summernoteScripts-->
    </body>
    </html>

    Then pass the file location in your HtmlEditor > HtmlEditorOptions> filePath parameter to load it.

  2. I think to change the font you need to select the text and then change it. I could be wrong though, I haven't tested this behavior in a while.

FYI I didn't test this, don't have much time right now. If it still doesn't work I'll try to get a working solution tomorrow.

and one more thing, on real device, it not showing the editor, tool are showing and it's working perfectly in emulator did need to add any kind for permission of something else. please help

Can you try loading the example app? If the example app works then there is some issue in your configuration.

Ashish-Raturi commented 3 years ago

WhatsApp Image 2021-06-12 at 8 14 45 AM

here is the image, it's not showing nothing on editor.

tneotia commented 3 years ago

Hmm do you get any logs when opening up the editor? I have no issues on my samsung device

tneotia commented 3 years ago

Okay I tried to come up with an example and I was successful. I also figured out why nothing was showing on the editor. Change your app like this: pubspec.yaml:

flutter:
  assets:
    - assets/

create assets/summernote.html:

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <meta name="description" content="Flutter Summernote HTML Editor">
    <meta name="author" content="tneotia">
    <title>Summernote Text Editor HTML</title>
    <script src="file:///android_asset/flutter_assets/packages/html_editor_enhanced/assets/jquery.min.js"></script>
    <link href="file:///android_asset/flutter_assets/packages/html_editor_enhanced/assets/summernote-lite.min.css" rel="stylesheet">
    <script src="file:///android_asset/flutter_assets/packages/html_editor_enhanced/assets/summernote-lite.min.js"></script>
    <style>
        @font-face {
            font-family: 'Billabong';
            font-style: normal;
            font-weight: 400;
            src: local('Billabong'), url('https://fonts.cdnfonts.com/s/13949/Billabong.woff') format('woff');
        }
        you need to add the other fonts here!!
    </style>
</head>
<body>
<div id="summernote-2"></div>
</body>
</html>

HtmlEditor:

HtmlEditor(
  controller: controller,
  htmlEditorOptions: HtmlEditorOptions(
    filePath: "assets/summernote.html",
  ),
  htmlToolbarOptions: HtmlToolbarOptions(
    defaultToolbarButtons: [
      StyleButtons(),
    ],
    customToolbarButtons: [
      Container(
        padding: const EdgeInsets.only(left: 8.0),
        height: kMinInteractiveDimension,
        decoration:
            BoxDecoration(
                color: Theme.of(context).scaffoldBackgroundColor,
                border: Border.all(
                    color: Theme.of(context)
                        .colorScheme
                        .onSurface
                        .withOpacity(0.12))),
        child: CustomDropdownButtonHideUnderline(
          child: CustomDropdownButton<String>(
            menuMaxHeight: MediaQuery.of(context).size.height / 3,
            menuDirection: DropdownMenuDirection.down,
            items: [
              CustomDropdownMenuItem(
                value: 'Courier New',
                child: PointerInterceptor(
                    child: Text('Courier New',
                        style: TextStyle(fontFamily: 'Courier'))),
              ),
              CustomDropdownMenuItem(
                value: 'sans-serif',
                child: PointerInterceptor(
                    child: Text('Sans Serif',
                        style: TextStyle(fontFamily: 'sans-serif'))),
              ),
              CustomDropdownMenuItem(
                value: 'Times New Roman',
                child: PointerInterceptor(
                    child: Text('Times New Roman',
                        style: TextStyle(fontFamily: 'Times'))),
              ),
              CustomDropdownMenuItem(
                value: 'Billabong',
                child: PointerInterceptor(
                    child: Text('Billabong',
                        style: TextStyle(
                          fontFamily: 'Billabong',
                        ))),
              ),
              //add your other fonts here!!
            ],
            value: 'Courier New',
            onChanged: (String? changed) async {
              if (changed != null) {
                controller.execCommand('fontName', argument: changed);
              }
            },
          ),
        ),
      )
      //add the other widgets here!!
    ],
  ),
),

To simplify my testing I didn't include font asset files in the Flutter app and I did not make it so that updating the font style changes the selected item for the dropdown. You should re-implement those functionalities.

View screenshot ![image](https://user-images.githubusercontent.com/50850142/121788279-1012c600-cb9a-11eb-92ce-dac8af083c65.png)

Edit:

I think its a good idea to add some sort of callback/listener/stream to the package so custom UI elements can be updated.

This has been added to the package to make your code a bit easier when you make the custom button :)

Ashish-Raturi commented 3 years ago

bingo, it's worked, thanks bro, you are the best. :) keep it up, have a great day.

AceChen1 commented 2 years ago

@tneotia @Ashish-Raturi hey , looks like this code is not work, may i know need copy those resource to my project's asset ? i am using ios device to do the POC

   <script src="assets/packages/html_editor_enhanced/assets/jquery.min.js"></script>
   <link href="assets/packages/html_editor_enhanced/assets/summernote-lite.min.css" rel="stylesheet">
   <script src="assets/packages/html_editor_enhanced/assets/summernote-lite.min.js"></script>
tneotia commented 2 years ago

The package asset path may be different on iOS. You can copy those assets to your project if you'd like, then the code becomes

<script src="assets/jquery.min.js"></script>
   <link href="assets/summernote-lite.min.css" rel="stylesheet">
   <script src="assets/summernote-lite.min.js"></script>
AceChen1 commented 2 years ago

The package asset path may be different on iOS. You can copy those assets to your project if you'd like, then the code becomes

<script src="assets/jquery.min.js"></script>
   <link href="assets/summernote-lite.min.css" rel="stylesheet">
   <script src="assets/summernote-lite.min.js"></script>

@tneotia but i am afraid if copy those assets to my project, the plugin's version upgrade need more effort to do that, for i believe that those source file have a big chance will be updated in the future.

tneotia commented 2 years ago

Actually no, those source files are hardly ever updated. They will likely only be updated when there is a Summernote version upgrade, which is very infrequent.

dishak331 commented 2 years ago

How to get those file names that you are using in your custom html code? we are using that and it is not doing anything. @tneotia

Isra0210 commented 2 years ago

I used this example, but it doesn't show the editor :/

tneotia commented 2 years ago

How to get those file names that you are using in your custom html code?

These are bundled into the package itself, if you are building Android (not web or iOS), it should work fine. Web and iOS will have different paths.

I used this example, but it doesn't show the editor :/

Check the logs, there might be some error that provides a clue what the issue is

Isra0210 commented 2 years ago

How to get those file names that you are using in your custom html code?

These are bundled into the package itself, if you are building Android (not web or iOS), it should work fine. Web and iOS will have different paths.

I used this example, but it doesn't show the editor :/

Check the logs, there might be some error that provides a clue what the issue is

I tried using the web, but it didn't work. does anyone have an idea how I can customize font style on the web?

jdayssol commented 1 year ago

For the web, the solution of tneotia is correct, but it is important to import the font in summernote.html in the ttf format.

jdayssol commented 1 year ago

like this @font-face { font-family: 'Georgia'; font-style: normal; font-weight: 400; src: local('Georgia'), url('./font/Georgia.ttf') format('ttf'); }

shabbirdudhiya commented 1 year ago

Can you give me example code for this? I tried implementing this but the ui is not updating ...I can't figure out what am i doing wrong.

  1. if i give the filePath to summernote.html file i am not able to write anything and cannot access the editor
  2. if i remove it i can access the editor but when i select my font the text inside the editor won't update

    
    HtmlEditor(
          htmlEditorOptions: const HtmlEditorOptions(
            // filePath: "assets/summernote.html",
            shouldEnsureVisible: true,
            hint: "Your text here...",
          ),
          htmlToolbarOptions: HtmlToolbarOptions(
            defaultToolbarButtons: [const StyleButtons()],
            customToolbarButtons: [
              Container(
                padding: const EdgeInsets.only(left: 8.0),
                child: CustomDropdownButtonHideUnderline(
                  child: CustomDropdownButton<String>(
                    menuMaxHeight: MediaQuery.of(context).size.height / 3,
                    menuDirection: DropdownMenuDirection.down,
                    items: const [
                      CustomDropdownMenuItem(
                        value: 'Kanz',
                        child: Text(
                          'Kanz',
                          style: TextStyle(
                            fontFamily: 'Kanz',
                          ),
                        ),
                      ),
                      CustomDropdownMenuItem(
                        value: 'Badri',
                        child: Text(
                          'Badri',
                          style: TextStyle(
                            fontFamily: 'Badri',
                          ),
                        ),
                      ),
                    ],
                    value: 'Kanz',
                    onChanged: (String? changed) async {
                      if (changed != null) {
                        if (kDebugMode) {
                          print(changed);
                        }
                        _.controller.execCommand('fontName', argument: changed);
                      }
                    },
                  ),
                ),
              )
            ],
            toolbarPosition: ToolbarPosition.aboveEditor,
            onDropdownChanged: (DropdownType type, dynamic changed,
                Function(dynamic)? updateSelectedItem) {
              if (kDebugMode) {
                print("dropdown '${describeEnum(type)}' changed to $changed");
              }
              return true;
            },
          ),
          callbacks: Callbacks(),
          controller: _.controller, //required
        ),