Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Design a dynamic platform theming mechanism between Material and Cupertino #8410

Closed
xster opened this issue Feb 25, 2017 · 27 comments
Closed

Design a dynamic platform theming mechanism between Material and Cupertino #8410

xster opened this issue Feb 25, 2017 · 27 comments

Comments

@xster
Copy link
Member

@xster xster commented Feb 25, 2017

Provide an easy way for developers to insert a simple widget whose UI conforms to Material or Cupertino depending on runtime platform.

@xster
Copy link
Member Author

@xster xster commented Mar 28, 2017

One concrete example for a semi-complex case where we probably want the experience to be magical-ish without too much user selection is with page routes.

Currently the dependency is:

material.MaterialPageRoute -> material._MountainViewPageTransition
                           -> material._CupertinoPageTransition

So the graph is
user's app
material [_MountainViewPageTransition, _CupertinoPageTransition]
widget

We'd need to clear up the dependency graph a bit. Starting from the graph down, we probably want:
user's app
platform dynamic
material / cupertino
widget

So one unpolished idea might be to do something like:

platform_widget(or something).PlatformPageRoute -> material.MaterialPageTransition
                                                -> cupertino.CupertinoPageTransition

which implies a user API change from pushing MaterialPageRoute to PlatformPageRoute.

or we can do something like:

material.MaterialPageRoute -> widget.PageTransition => platform_widget.PlatformPageTransition -> material.MaterialPageTransition
                                                                                              -> cupertino.CupertinoPageTransition

which implies we're saying the users don't have to manually think about page transitions and the Material spec is automatically platform dynamic. We'd also have to do the dependency injection of widget.PageTransition -> platform_widget.PlatformPageTransition in the MaterialApp.

@xster
Copy link
Member Author

@xster xster commented Mar 28, 2017

https://material.io/guidelines/patterns/navigational-transitions.html
Doesn't really say anything about any platform specific page transitions and basically says all parent->child transitions are hero transitions. If we take it literally, we still need to do something for #8726 since not every user wants to strictly adhere to Material design.

Which makes me lean more towards option 1 where the user specifies that he wants a platform dynamic page route (and perhaps will be overriden with a hero navigation observer most of the time).

cc @HansMuller

@abarth
Copy link
Contributor

@abarth abarth commented Mar 28, 2017

Material Design has a notion of adapting to the target platform. That means it's conceptually fine for material.dart to take a dependency on cupertino.dart and for MaterialPageRoute to know about both _MountainViewPageTransition and _CupertinoPageTransition.

Another way to approach this thought experiment is to build a pure CupertinoPageRoute and then see what parts of its machinery MaterialPageRoute wants to use in order to adapt to iOS.

@HansMuller
Copy link
Contributor

@HansMuller HansMuller commented Mar 28, 2017

Developers may want to control the mapping from generic components to potentially platform-specific ones. In other words rather than providing generic components that just map from the platform to some platform-specific component (or transition or whatever), developers might prefer for control to flow through a look-and-feel theme that they can customize. Maybe I'd like generic buttons to map to MyButton and route transition durations to be 500ms. If the l&f theme was an inherited widget I could configure for the app or a subtree.

@xster
Copy link
Member Author

@xster xster commented Mar 28, 2017

@HansMuller that's always going to be true in any of the options. Users can always pick a specific route or transition. If there were dynamic wrappers, they're just a thin convenience layer.

@xster
Copy link
Member Author

@xster xster commented Mar 28, 2017

Chatted offline. So conceptually, instead of making Cupertino and Material siblings with each meaning a full spec'ed design opinion and components for iOS and Android and having a common parent, we'll extract MountainView out of Material and make MountainView and Cupertino siblings. Those will be the basic OS components that have less value add if the developers remade them. Material will be the opinionated design usage of MountainView and Cupertino base components. And the user that wants to create non-Material apps can reuse MountainView and not have to recreate non-value-add basic components.

(cc @sethladd https://docs.google.com/presentation/d/1cw7A4HbvM_Abv320rVgPVGiUP2msVs7tfGbkgdrTy0I/edit?ts=58d9dd75#slide=id.gbb3c3233b_0_162)

@pspeter3
Copy link

@pspeter3 pspeter3 commented Mar 16, 2018

Is there any documentation about rendering the correct widgets for each platform? I thought there was but I'm not sure anymore.

@pascalwacker
Copy link

@pascalwacker pascalwacker commented Mar 23, 2018

Is this behaviour set in the MaterialApp, theme property, like
theme: new ThemeData( platform: Theme.of(context).platform == TargetPlatform.iOS ? TargetPlatform.iOS : TargetPlatform.android ),

@xster xster added this to Pending O(weeks) in xster tasks Mar 29, 2018
@kowsheek
Copy link

@kowsheek kowsheek commented Apr 2, 2018

@pspeter3 I don't think there was. After much digging the closest thing I could find was this by @emshack 👏

@xster xster moved this from Pending O(weeks) to In-Progress O(days) in xster tasks May 16, 2018
@xster xster self-assigned this May 16, 2018
@xster xster moved this from In-Progress O(days) to Holding for review in xster tasks May 23, 2018
@xster
Copy link
Member Author

@xster xster commented May 25, 2018

We're discussing about a solution for this now.

For those that are interested on the thread, can you guys provide some specific examples (even better with usage code) of what behaviors you'd like to see from Flutter?

Especially if you would like additional functionalities than Flutter providing a widget that simply does:

class PlatformBuilder extends StatelessWidget {
  const PlatformBuilder({
    this.materialWidgetBuilder,
    this.cupertinoWidgetBuilder,
  });

  final WidgetBuilder materialWidgetBuilder;
  final WidgetBuilder cupertinoWidgetBuilder;

  @override
  Widget build(BuildContext context) {
    switch (Theme.of(context).platform) {
      case TargetPlatform.android:
        return materialWidgetBuilder(context);
      case TargetPlatform.iOS:
        return cupertinoWidgetBuilder(context);
    }
  }
}
@pspeter3
Copy link

@pspeter3 pspeter3 commented May 25, 2018

Either that or providing people a way to think about cross platform well. Right now, I feel like I would either write many Builders similar to what you wrote or leave switch statements scattered across the code.

@mpiannucci
Copy link

@mpiannucci mpiannucci commented Jun 17, 2018

I recently struggled with this, and kinda ditched it because it got a little too tedious for me.

All I really want is a way to get Platform Specific AppBar's (ios Navbars), BottonNavigtionBar (ios TabBar), spinners, and dialogs without extra effort. Those are the main pieces that really stand out to me that I feel like MUST be in the native design language.

@jeanluc243
Copy link

@jeanluc243 jeanluc243 commented Aug 7, 2018

👏

@Hixie
Copy link
Member

@Hixie Hixie commented Aug 14, 2018

cc @willlarche - you may be interested in seeing above what some of our developers are saying they would like from Flutter in terms of automatic cross-platformness (beyond what Material is defined as doing today).

@xster xster removed their assignment Sep 13, 2018
@Hixie Hixie modified the milestones: Goals, Stretch Goals Oct 16, 2018
@giacomocerquone
Copy link

@giacomocerquone giacomocerquone commented Oct 25, 2018

Where are we with this?
I was reading 10 minutes ago this topic on google groups: https://groups.google.com/forum/#!topic/flutter-dev/UOqzbSpurqg

Basically what "Adam Barth" was saying is that changing design language, can change the functionalities of the single widgets and it's right but not for most of them.
A dialog is a dialog and they differ only in the way they're styled and it's so for around 85% of the widgets.

At the expense of clarity, I see two roads:

  1. Apply as default the behaviour showed by @xster only on the "safe" widgets (safe meaning here that I can't attach a function on an inexistent behaviour of another design language of the same widget). This creates a weir behaviour for the user that don't know this.
  2. Create "n" extra components that are called somewhat like: "PlatformDialog", "PlatformActionSheet" or "PlatformButton" that does nothing more of the switch proposed above.

I'd go for the second

But not having this functionality built-in, I know for sure is not good for a project's codebase. I've seen many flutter apps doing this switch manually over the flutter widgets.

Tell me yours :)

@wenerme
Copy link

@wenerme wenerme commented Oct 25, 2018

second pleases. https://ionicframework.com display platform styled widget, but expose the same interface.

@pascalwacker
Copy link

@pascalwacker pascalwacker commented Oct 25, 2018

I would also prefer the second option. If there are platform specific settings these widgets could have a property like cupertinoFooBar where you can pass in additional arguments.

@rajkar86
Copy link

@rajkar86 rajkar86 commented Nov 1, 2018

I'd prefer the second option as well.

@manhluong
Copy link

@manhluong manhluong commented Nov 9, 2018

Second option for me as well.

If I am not wrong, it should be something like what this library is doing: https://pub.dartlang.org/packages/flutter_platform_widgets

@Hixie
Copy link
Member

@Hixie Hixie commented Feb 27, 2019

I'm curious about what people would actually like here. For something like the back button, we do this automatically already. For something a little more elaborate, like a regular text button, one could imagine a control that automatically switches between Cupertino and Material variants. But what about more complicated things? For example, the Switch control is a different size on iOS and Material, what should happen when you use that? Should it change size? Be the biggest size? What about something like a checkbox, which exists on Material but not iOS? Or Navigator, which works entirely differently in Material and iOS paradigms?

@xster
Copy link
Member Author

@xster xster commented Feb 27, 2019

To somewhat second Hixie, as we try to make this more concrete, it's becoming clearer that these are questions that need to be answered at design time rather than implementation time (and even less so doing it implicitly).

Even for relatively simple things like sliders and dialogs, in Material, sliders can have manually editable numeric fields, iOS does not. With dialogs, Material specs against having more than 2 responses while UIAlertController supports a vertical scrollable list of actions when there are more than 2 (though discouraged). The interchangeability of dialog/alerts and action sheets is also greater on iOS and requires more design level subjectivity. What should the API be in a common widget?

At mid-level complexity: the Material app bar supports toggling into contextual actions mode and having stateful actions (such as the pin button in Google Inbox). iOS's navigation bar commonly has segmented controls in lieu of a title. Material bottom sheets are highly flexible, can be dismissible without choosing any action, can be scrollable/expandable, can even expand into full screens themselves. iOS action sheets are always modal, can't contain arbitrary widgets and aren't even necessarily anchored at the bottom of the screen on iPads. What to do in each case are answers your apps' designers need to answer.

At your application's information architecture level, the 2 designs can diverge on the general pattern. In iOS, tab bars with parallel navigation stacks are common. Page navigations occur inside of tabs generally but you can also summon full screen modals that presents pages over the tabs. With Material, what the HIG calls "Flat Navigation" isn't common practice and lateral navigation happens in one page at the hierarchy root level via commonly the drawer. This can create entirely different app experiences. You can navigate to the same sub-item from a feed in tab 1 and from search in tab 2 on iOS. You can go into a profile tab in tab 3, change some app-wide setting and go back to tab 1. These interactions aren't possible if lateral navigation only happen at the root. These are all product/design level questions that are likely too late to try to answer at implementation time.

Given the need to make design choices, there are 2 choices we can take (and get community feedback on).

1- Present an opinionated and (arbitrarily) shrunk down set of widgets that takes the minimally supported API options from both while making compromises clearly documented. e.g. there's always 2 choices on dialogs, common app/nav bars can't have iOS 11 style large titles or be translucent etc. Since these design choices aren't inherently related to Material or Cupertino themselves, they can't be in either packages and should be separate Flutter package in pub.
2- Produce a design spec that specifies what happens in the harder cases such as the app's IA such that it's coherent in both cases. Though this is clearly a harder task and could still imply some design choices are either excluded (no sliders with manual value edits) or not platform adapting (only simple bottom/action sheets. If you try to use expandable, you need to fallback to straight Material design).

Orthogonally to all of this, some UI are always going to be 'wrong' if not done, such as back swipe navigations on iOS or iOS scroll physics on Android. For those, we always auto-adapt to the platform.

@xster
Copy link
Member Author

@xster xster commented May 7, 2019

In order to move this issue forward and to take concrete action based on user feedback, I've created a doc http://bit.ly/flutter-adaptive-widget-problem to clarify terminology around this nebulous concept and hopefully break it down into smaller bits.

Please give us your feedback in terms of what specifically is desired with respect to 'dynamic adaptations'. Once we have some feedback, we can close this issue and break it down into smaller specific actionable issues.

@ShadyBoukhary
Copy link

@ShadyBoukhary ShadyBoukhary commented May 27, 2019

@xster The document you provided is very helpful. I think automatic switching on types 3 and 4 is very helpful. In addition, just to add to the conversation, something like https://pub.dartlang.org/packages/flutter_platform_widgets
would also be incredibly helpful.

@no-response
Copy link

@no-response no-response bot commented May 31, 2019

Without additional information, we are unfortunately not sure how to resolve this issue. We are therefore reluctantly going to close this bug for now. Please don't hesitate to comment on the bug if you have any more information for us; we will reopen it right away!
Thanks for your contribution.

@xster
Copy link
Member Author

@xster xster commented Jun 4, 2019

We haven't received any actionable feedback from http://bit.ly/flutter-adaptive-widget-problem so we're closing this issue and branching off the subset (type 4) of the problem that's solvable in #33830.

@ShadyBoukhary, for type 3, specifically based on http://bit.ly/flutter-adaptive-widget-problem, which compromises would you like us to make (such as in the dialog/dropdown/action sheet/picker/pop-up many-to-many scenario)?

@chornbe
Copy link

@chornbe chornbe commented Jul 10, 2019

This feels a lot like the discussions when Swing was first introduced.

Basically, what people WANT is the ability to say something like ( child: Text( “some text” ) ) and have the platform draw an OEM or OEM-ish textbox without thinking any more about it. When you build a material app you’re essentially agreeing to have the whole thing look like Android unless you take other actions. The DESIRE in the community is for it to look like the device’s native design language without taking extra steps.

That’s hard. But that’s what people want. Adding class decoration or different types just makes for clunkier code, and IMO, should only be necessary when you really, really want to decorate something in a very particular way.

For me and my company, we just say screw it... you get an Android-looking app while we continue to evaluate what Flutter really does for us that we can’t get with Native, beyond just the kitsch of saying we can work in Flutter. Which not one single customer has ever heard of (yet). :)

The hope of “write once, run everywhere” is still very much in its youth because every time you blink, another new company, another new platform, another new “better idea” pops up, and now you’ve got to decide between Ionic, React Native (bleh), Trigger, Flutter, and... let’s see what’s next.

Great goal. Competing platforms may drive platform enhancements and updates and innovations, but also further fragments the landscape.

At least Flutter came from one of the big campuses, and it’s pretty much the only cross-platform tool that has. That’s power. I don’t see Apple ever building XCode for Android, and native Android development is akin to eating a live rattlesnake, so at the very least, Flutter brings WAY more approachability to “native”-enough development for Android. If nothing else ever comes of it in terms of cross-platform adoption, THAT ALONE is well, well worth the price of admission.

$.02

@galeyang
Copy link

@galeyang galeyang commented Nov 5, 2019

Updated: this sign-up form has been closed. Thank you for your interests!


Hello from the Flutter team!

If you’re following this discussion, we are looking for a few Flutter developers to share their experience on developing Flutter apps that have a platform-specific look & feel for both iOS and Android.

If you’re interested, please fill out this short questionnaire: [closed]

Thank you very much for your help!

@xster xster moved this from Structural Backlog to Done in iOS Framework Feb 27, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
xster tasks
Holding for review
iOS Framework
  
Done
Linked pull requests

Successfully merging a pull request may close this issue.

None yet