super_editor icon indicating copy to clipboard operation
super_editor copied to clipboard

iOS caret color in dark mode not as expected (barely visible)

Open simonbengtsson opened this issue 1 year ago • 21 comments

Package Version "super_editor, GitHub, stable branch"

To Reproduce

  1. Run example editor with iOS in darkmode
  2. Toggle to dark theme with FAB button

Actual behavior The caret is black and hard to see since editor background is a dark grey.

Expected behavior The caret to be visible. I expect the caret color to be red since this is set in the caretStyle and what is happening on macOS.

Platform iOS. The issue is not happening on mac or web (or it does but the caret gets the correct color after refocusing the editor). Not tried other platforms.

Flutter version Flutter 3.22.0 • channel stable Framework • revision 5dcb86f68f (2 weeks ago) • 2024-05-09 07:39:20 -0500 Engine • revision f6344b75dc Tools • Dart 3.4.0 • DevTools 2.34.3

Screenshots issue

This is the same problem as described in https://github.com/superlistapp/super_editor/issues/1347, but I can reproduce every time on iOS.

simonbengtsson avatar May 26 '24 10:05 simonbengtsson

@simonbengtsson To update the caret color on iOS, you need to provide a handleColor to SuperEditorIosControlsController.

That way, you can customize the caret for iOS:

angelosilvestre avatar Jun 06 '24 23:06 angelosilvestre

Got it! So SuperEditorIosControlsController.handleColor on iOS, SuperEditorAndroidControlsController.controlsColor on android and DefaultCaretOverlayBuilder.caretStyle.color on all other platforms.

Since SuperEditorIosControlsController.handleColor and SuperEditorAndroidControlsController.controlsColor are final I assume then that the idea is that you create new controllers and dispose the old one if the theme changes?

simonbengtsson avatar Jun 09 '24 15:06 simonbengtsson

Since SuperEditorIosControlsController.handleColor and SuperEditorAndroidControlsController.controlsColor are final I assume then that the idea is that you create new controllers and dispose the old one if the theme changes?

@simonbengtsson Yeah, you'll need to do that.

angelosilvestre avatar Jun 11 '24 01:06 angelosilvestre

@simonbengtsson did that work for you? If so, can you paste the code that you had to write to get the effect you want? I'd like for us to take a look at how much work that is and see if it makes sense to create an easier pathway.

matthew-carroll avatar Jun 18 '24 00:06 matthew-carroll

Actually no. When reassigning the _iosControlsController in the build method it crashes with the exception below. It feels wrong to reassign a controller in the build method so make sense I guess. Have no immediate idea for where else to do it however. For now we have set the cursor color to always be our primary color which kind of works in both dark and light mode.

OverlayPortalController.show() should not be called during build.
'package:flutter/src/widgets/overlay.dart':
Failed assertion: line 1791 pos 7: 'SchedulerBinding.instance.schedulerPhase != SchedulerPhase.persistentCallbacks'

My attempt was just adding the following lines to the _buildEditor method in example_editor.dart L370.

  Widget _buildEditor(BuildContext context) {
    final isLight = Theme.of(context).brightness == Brightness.light;

    // Added code
    _iosControlsController.dispose();
    _iosControlsController = SuperEditorIosControlsController(
      handleColor: isLight ? Colors.black : Colors.redAccent,
    );

    [...]

simonbengtsson avatar Jun 18 '24 10:06 simonbengtsson

@simonbengtsson You should try to update it on the didChangeDependencies method.

angelosilvestre avatar Jun 18 '24 23:06 angelosilvestre

Tried now, but didChangeDependencies was not called when "Change theme" button in the example editor is clicked. Used the following code:

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    final isLight = Theme.of(context).brightness == Brightness.light;
    _iosControlsController.dispose();
    _iosControlsController = SuperEditorIosControlsController(
      handleColor: isLight ? Colors.black : Colors.redAccent,
    );
    print('Reassigned ${isLight}'); // Only called once, not on theme change
  }

simonbengtsson avatar Jun 19 '24 09:06 simonbengtsson

@simonbengtsson @matthew-carroll I took a look at this. The didChangeDependencies isn't been called because used there isn't the themed context. The ExampleEditor's ancestor theme doesn't change from light to dark, only the theme that is inside ExampleEditor, around the editor, that changes.

To solve this, the theme should be moved up, outside of ExampleEditor.

@override
Widget build(BuildContext context) {
  return ValueListenableBuilder(
    valueListenable: _brightness,
    builder: (context, brightness, child) {
      return Theme(
        data: ThemeData(brightness: brightness),
        child: child!,
      );
    },
    child: Builder(
      // This builder captures the new theme
      builder: (themedContext) {
      ...
     }   
   ),
 );
}

angelosilvestre avatar Jun 20 '24 00:06 angelosilvestre

@angelosilvestre if that resolves the issue in the demo app, let's go ahead and make the change. Then let's take another look at how much code is involved with such a simple behavior.

matthew-carroll avatar Jun 20 '24 05:06 matthew-carroll

@matthew-carroll After taking a better look, I noticed that SuperEditorIosHandlesDocumentLayerBuilder has a handleColor property, so we shouldn't need to rely on SuperEditorIosControlsController for that.

However, I noticed this isn't working correctly even for desktop. The color doesn't update immediately:

https://github.com/superlistapp/super_editor/assets/7597082/d77e432a-331c-4d06-a24c-068b546abcd8

This looks like a deeper problem in ContentLayers. Maybe it isn't working correctly when changing the layer builders?

angelosilvestre avatar Jun 24 '24 23:06 angelosilvestre

@angelosilvestre can you dig deeper and try to find out where the problem is?

matthew-carroll avatar Jul 01 '24 05:07 matthew-carroll

@matthew-carroll After taking a deeper look, I found that the cause is that performLayout isn't being called in ContentLayers. As a result, the layer builders aren't re-creating the layers.

When switching between light and dark mode, the only thing that we change in the editor is the font color. Because of that, only markNeedsPaint gets called, and not markNeedsLayout. We can confirm this by also changing the font size in the dark mode styles. If the font size changes, the layout is invalidated and the layer builder recreates the layer.

A possible solution can be achieved by making every SuperEditorLayerBuilder implement the == and then on SuperEditorState.didUpdateWidget checking if any layer builder changed. If a layer builder changed, we should call markNeedsLayout on the ContentLayers render object.

What do you think of that approach? Do you have any other ideas?

angelosilvestre avatar Aug 21 '24 01:08 angelosilvestre

@angelosilvestre I'm not entirely sure about the implications here, but on the surface of the issue, I don't know why we'd want to re-run layout for color changes. Layout is generally expensive and we should only run it when layout truly changes.

Is there a particular reason that you're suggesting we re-run layout for color changes?

matthew-carroll avatar Oct 18 '24 18:10 matthew-carroll

Because without a re-run of the layout, the overlayers won't be rebuilt, so they won't pick the new colors.

angelosilvestre avatar Oct 18 '24 22:10 angelosilvestre

To help me understand exactly what's happening here, can you please write a failing test and open a PR with that test failure?

matthew-carroll avatar Oct 18 '24 23:10 matthew-carroll

@matthew-carroll Opened https://github.com/superlistapp/super_editor/pull/2388

angelosilvestre avatar Oct 30 '24 00:10 angelosilvestre

I set handlerColor when in dark mode the cursor color still black....

lucasjinreal avatar Nov 15 '24 16:11 lucasjinreal

@simonbengtsson @lucasjinreal This should now work as expected. Please see this comment https://github.com/superlistapp/super_editor/issues/2302#issuecomment-2566422439

angelosilvestre avatar Dec 31 '24 12:12 angelosilvestre

Hi, can u point out which version start from could fix this?

lucasjinreal avatar Jan 01 '25 02:01 lucasjinreal

@lucasjinreal We didn't release a new pub version yet, but you can add a dependency to the latest main.

angelosilvestre avatar Jan 04 '25 18:01 angelosilvestre

@matthew-carroll I believe we can close this issue. It was already fixed and we have new releases that include the fix.

angelosilvestre avatar Nov 10 '25 16:11 angelosilvestre