rydmike / flex_color_picker

A highly customizable Flutter color picker.
BSD 3-Clause "New" or "Revised" License
200 stars 42 forks source link

Ability to set selected color #14

Closed metalinspired closed 3 years ago

metalinspired commented 3 years ago

Unless I'm missing on how to do it, it would be nice to have a way of changing currently selected color. I have a dialog (rolled my own, not using showPickerDialog) with color wheel where I would like to have a reset button that would set the color in the wheel to something predefined.

rydmike commented 3 years ago

Hi, thanks for your question. 👍🏻

When you create the color picker with ColorPicker(color: myStartColor, ...), the color property is used to pre-select the starting color, here represented by myStartColor

That's basically it. 💙

More and related info

This provided start color can be any color. The ColorPicker will always try to find the picker type, from all the picker types you enabled, like Material primary, Material accent, custom swatches or Wheel, that can best represent the provided color and use it to show the initial color when it is opened.

For example, if the Material and Accent and Wheel pickers are enabled and the picker also shows their shade (on by default) colors, and the provided color is not a Material primary color, Accent color or any of their shades, then the wheel picker will be used to pre-select and show the color automatically.

If the provided color was one of the built in Material primary or accent colors or one of its shades, than the color will be shown on the corresponding picker type, the color will be selected and that picker will be shown.

If you have a built-in Material color selected and move to the Wheel, the same color and its shades is still shown on the Wheel, and if you click on the shades under the Wheel it will use the built in Material colors shades and show where they are on the HSV Wheel. Only when you select a color on the wheel that is not any of the built-in ones (or a color swatch you added to custom) ones, does it compute a color swatch and always puts the wheel selected color as the base [500] index in the computed color swatch.

If you on the Wheel happen to select a standard Material color or any of its shades, it will show those shades under Wheel, but it will not move to the corresponding Material picker automatically, even if it it corresponding Material picker happens to be enabled. However, if you move to it manually, you will find it selected there as well.

More info, but a bit off topic

When it comes to using the color picker dialog, and using the onChanged callback, while it is powerful and allows for updating Widgets and even theme in the background as the dialog is open and colors are selected it, this is sometimes not an always needed use case. The stable release of 2.0.0 will include a new optonal color picker function that just returns a color without the need for a callback (discussed here https://github.com/rydmike/flex_color_picker/issues/13), that way does however not allow for updating the colors of Widgets or even themes in the background as updates are done in the picker dialog, you only get the selected color back when the dialog is closed. However in many use cases this might be the only thing that is needed, and for those use cases it new dialog API will be simpler to use.

This feature, and many others for version 2.0.0 is actually done and ready. I'm just working on the docs a bit more and will make one more pre-release of 2.0.0 before I release the stable version.

Edit: I just released the last pre-release of 2.0.0-nullsafety.5, I'm just going to do some more documentation review before I release as stable 2.0.0.

Conclusion

While using the color property to set the start color is certainly shown in current docs, I will review the new docs to see if it can be made more clear. Since I'm also adding a new example to main example file for using the new optional picker dialog API, I will use a start color in its example that by default opens the wheel instead of the built-in Material color pickers, just to demo this as well.

Thanks again for your question and feedback. I hope the answer helps, let me know if you need more info. If you are satisficed with the reply, then please feel free to close the issue. 😃

metalinspired commented 3 years ago

Hi @rydmike.

And thank you for your detailed answer. However I do believe I was misunderstood. Setting starting color is crystal clear. What I was referring to was setting the current color in color picker, after it is already created, from outside of it. In my particular case user can click on a button to open a dialog with color picker, starting with currently set color. What I would like to achieve is to have a button that, in that dialog, to (re)set the color to something predefined. So color picker is shown, user selected some color (or not but picker is set to current color using color property) and he decides that the color he chosen is not really to his liking and would like to reset it to default color.

Regards

rydmike commented 3 years ago

Hi @metalinspired,

I see and thanks for the clarification, it certainly helped with understanding your question. Yes communication is difficult, I know 😃

Yes you are right, if you use the picker in a dialog you cannot set its color again by giving the color property a new value, since you have no way of getting it in there when you are in a modal dialog.

If you use the picker on desktop/wen where you potentially could just have the picker on the screen (like in the web demo), then you can actually do that, so you could make buttons outside the picker that sets the color from outside the picker to any color of your choice.

What you describe does sound a bit like an undo feature, or a way to revert back to default/given good colors that are not part of the standard Material palette.

Custom colors

For the latter part you can add your "good" or "safe" backup colors to the custom picker. You don't have to call the picker tab "Custom", it could be "Default" "Company" "Appname" or whatever and hold only the colors that are relevant, for example part of company visual guide, or app default colors, to the custom picker. The user can then easily go back to these colors.

You can read more about setting up custom colors here: https://pub.dev/packages/flex_color_picker/versions/2.0.0-nullsafety.5#custom-color-swatches

And here regarding the "tab" name for the custom choice: https://pub.dev/packages/flex_color_picker/versions/2.0.0-nullsafety.5#customized-labels

This feature is also available in the latest stable 1.x release, the docs describing it better only exist in the 2.0.0 pre-release though.

Multi-level undo

What you really described above sounded to me like an undo function. For that you can enable the showRecentColors feature, when you set it to true and optionally specify how many recent colors you want to maintain with maxRecentColors, on the recently used colors history list, then you can use this as a multi level undo. With this feature enabled, as users selects colors, the previous color is added to the recently used colors list and the user can co back to any of them by clicking on them again.

This is fairly new feature, it is not available in the stable 1.x release, it is only available on the last two 2.0.0 pre-release versions. You can read more about it here: https://pub.dev/packages/flex_color_picker/versions/2.0.0-nullsafety.5#show-recent-colors

You can test it in action on the Picker's live v2 web demo here: https://rydmike.com/flexcolorpicker


The current 2.0.0-nullsafety.5 pre-release version will not get any additional changes. I will just review the docs and fix a bunch of typos before I release it as 2.0.0 stable release. Might get it released later today (local time) or tomorrow.

rydmike commented 3 years ago

FYI: Version 2.0.0 has now been released.

metalinspired commented 3 years ago

Hi @rydmike,

Yes communication can be hard, I work and live in Germany but my German is mediocre at best ;) Well yes you could call it undo but I would leave it to user to decide that, IMHO adding a method like setColor(Color) to picker would be enough. This is just a feature proposal so if/when it is implemented is totally up to you :)

Thank you and best regards.

rydmike commented 3 years ago

Hi @metalinspired ,

In your use case you mention opening the picker in a dialog:

What I was referring to was setting the current color in color picker, after it is already created, from outside of it. In my particular case user can click on a button to open a dialog with color picker, starting with currently set color. What I would like to achieve is to have a button that, in that dialog, to (re)set the color to something predefined. So color picker is shown, user selected some color (or not but picker is set to current color using color property) and he decides that the color he chosen is not really to his liking and would like to reset it to default color.

So the use case mentioned above are kind of covered.

External control when the ColorPicker is used in the built-in dialog

In the color picker's own pre-made dialog, there is really no interaction that could be used to call the proposed setColor(color). So for that case I don't really see how having it would help. Unless you make your own custom dialog, that includes the ColorPicker and some extra button(s) for that, then yes sure, in such a setup it would work and would be a very useful feature to have.

External control of the ColorPicker when used on the main surface

It is of course also very useful if you use the color picker on the main surface in a Web or Desktop app to be able to control its value externally, actually this is technically the same case as baking in a custom control and the picker in the same dialog wrapper.

I totally agree on this feature, it might even be a very common need on desktop/web apps. I had just not needed it or tried it myself yet. This should absolutely be supported for desktop/web apps where it makes a lot more sense too than on mobile phones. It is just kind of an oversight that it is not already supported, not at all difficult to enable.

2.0.1 update

So, if t is this kind of external update of the ColorPicker widget that you are really asking for and missing, then I just added support for updating the picker by just giving the color property a new value. It will now update correctly when the color is updated externally too. This might help!?

This new feature is available in latest release 2.0.1.

To the live Web demo I added a demo of this feature as well. You can control the on screen picker from a few fix colored boxes by clicking on them, and it updates the picker to those colors.

In the demo you can even "remote" control the on-screen picker from a dialog picker, so the on screen picker mirrors what you do in the dialog. Not as such so useful, but it was kind of a fun demo to add to it, and I used it to verify that it works well and behaves correctly.

web_color_picker_v2-0-1-small


Better now? 😃

metalinspired commented 3 years ago

Hi @rydmike,

Dear lord I only now realize I was stuck in PHP world when I was answering hence my setColor(Color) idea 😆 However what you did in 2.0.1 is exactly what I had in mind. Thank you very much.

rydmike commented 3 years ago

Thank you too @metalinspired. Your issue and point was a very valid one. A Flutter widget that keeps internal state should of course rebuild and react to input changes the way it does now.

There are a few reasons why it did not earlier, and they are all equally dumb, so I really appreciate this feedback and finding! 👍🏻

Just out of curiosity here is why it was not included from the start as it should have been.

This is mostly documented here for my own recollection, so I don't do silly things like this again 😃, you are welcome to reading it, or not, lol.

  1. I had only used the picker in a dialog, with the built-in dialog. In that use case, as discussed earlier, there is no way to use this feature. So personally I was not missing it (=I did not notice or think of that it was not updating the Widget if you changed the color), but that does not mean it should not be there. Of course it should.
  2. I did consider this use case earlier though. However, with my earlier versions and the color and onChanged callback pattern with onChanged in my apps also updating the input to color to do the updates of colors as they happen while the dialog is open. When doing so, using the didUpdateWidget and oldWidget was causing too many a bit expensive rebuilds for a state the Widget already had internally. So I just let the ColorPicker widget ignore the new color input value, since in considered use cases it knew what the value was anyway since it had just sent it to onChanged callback and updated its internal state to the same color value. Of course, if you add some other change to the color externally, that was then also getting ignored and that is what you found and pointed out.
  3. I had this kind of update support enabled in some intermediate, (maybe way) earlier versions too. However, when I optimized the rebuilds, I forgot about including this case, I think it was even included in a more unoptimized version pre-release.

The way it does it now is of course to check the color value against the internally kept state of the selected color, not the oldWidget. The oldWidget, color value may change a lot, commonly on every onColorChnaged call using it, is then also updating the color property in response to this. I the current implementation it does not matter though. If the incoming color is same as internal state (not the oldWidget as that will pretty much always have another color value), then it can still avoid triggering the a bit heavy extra updates that actually happens when the color is changed externally and it has to find the right picker and potentially compute a new swatch. What happens in that case is basically a repeat of stuff it does on initState.

Now it checks if the color value passed in is the same as internal state, (oldWidget.color is not same though), well then the update is just a part of the commonly used pattern, where eg color: myColor is used, we get a callback onColorCanged with new color, update myColor to it, do a setState to rebuild. If it is so then the ColorPicker widget now sees and knows this, and can avoid the extra work as before, and only go into the a bit more involved update if it was changed to something else than its internal state.

Well anyway basically adding this feature did not cause any extra overhead for normal use when you are not changing the color externally compared to its internally kept color, but when you do it will do a bit more logic to find the right picker, select and compute the swatch for it, if it has to. All the code to add support for this basically already existed, so the mods to support it was an if and few lines.

The reason why I added that "inception" color picker to the web demo was so I could test and see how expensive doing this extra work is in practice. specially if you do it all the time, like the dialog picker actually remote controlling the picker on the screen is doing in the web demo.

Turns out, even with that extra work, it runs very smoothly in release mode. I even tested building it on a super old Nexus 7 from 2013, it runs pretty much flawlessly on it in release mode too. So there was no reason to not enable this feature.

As a curiosity, I keep on old Nexus7 7" tablet from 2013 around to performance test stuff on it sometimes. If it builds and runs OK it in release mode on it, it will usually be butter smooth on anything a bit more current. It is also pretty cool that with Flutter you can run apps on an old Android 6.0.1 device, and they still work and look the same as on a new Android 10 device, you can't do that even with native Android 🚀 😄

Anyway, if you happen to read this, thanks for the good feedback and discussions. 💙