design_language 0.0.1 copy "design_language: ^0.0.1" to clipboard
design_language: ^0.0.1 copied to clipboard

discontinued

A package that makes cross-design-language development a breeze.

Welcome to design_language, a package that makes cross-design-language development a breeze.

Motivation #

Standard Flutter does not currently offer a straightforward approach for testing widgets that are targeted to other platforms. Usually developers use Platform.isIOS, Platform.isAndroid, ... to dispatch widgets. For example, let's say the widget tree on iOS should mainly consists of Cupertino widgets and on Android of Material widgets, while on macOS it should render widgets from macos_ui and on Windows those from fluent_ui. One would need 4 different machines to correctly test the UI.

However, what if you don't have an iOS device or a Mac? Or what if constantly switching between physical devices ends up being very time consuming and/or annoying? Wouldn't you prefer to to test all these different layouts directly from your main PC? Since all platforms can render widgets from any design language — e.g., Cupertino widgets can be rendered on any platform — why not take full advantage of that?

This package was made to cover all those scenarios: by introducing a DesignLanguageNotifier, whose state can be changed at runtime, we can force all widgets build with DesignLanguageDispatcher to be rebuilt.

The package was inspired by flutter_platform_widgets, which comes with a set of ready-to-use, cross-design-language widgets (such as PlatformAlertDialog, PlatformScaffold, ...), which is is a good starting point for learning how to build cross-design-language widgets. However, there are some limitation: for example, PlatformScaffold does not support the Material You variant. You would have to build it yourself anyway with PlatformWidget. This package — design_language — does not come with any prebuilt cross-design-platform widgets: it is up to the developers to determine the exact widget, per design language; this way it should be easier for your UI to adapt, over time.

Getting started #

You need to add design_language to your dependencies.

dependencies:
  design_language: ^latest # replace latest with version number

Usage #

This package aims to become the go-to package for multi-design-language support.

Currently, the following groups are supported:

  • Material and Cupertino
  • Material, Cupertino and Macos
  • Material, Cupertino, Macos and Fluent

In case a different design language group is needed (e.g. only Cupertino and Fluent), either open an issue or fork this repo and create a merge request.

Once you know which group to choose, go to the next step.

Library creation #

After installing the design_language package, there is one more step you need to do before being able to use the utils: exporting the correct "hidden" library.

Create a file inside your lib directory where global configs are done, e.g., lib/core/design/design_language.dart. Open it, and paste one of:

  • Material and Cupertino

    library design_language;
    export 'package:design_language/src/material_cupertino/library.dart';
    
  • Material, Cupertino and Macos

    library design_language;
    export 'package:design_language/src/material_cupertino_macos/library.dart';
    
  • Material, Cupertino, Macos and Fluent

    library design_language;
    export 'package:design_language/src/material_cupertino_macos_fluent/library.dart';
    

Now the library is visible and all the necessary utils are available.

The next steps assume that the selected design language group is Material, Cupertino and Macos.

DesignLanguage and DesignLanguageNotifier #

You need to instantiate DesignLanguage.notifier before you can use DesignLanguageNotifier or matchDesignLanguage (which are explained later). A good place would be inside your main function.

void main() {
  DesignLanguage.notifier =
      DesignLanguageNotifier(DesignLanguage.fromPhysicalPlatform);
  // the design language is reset upon every restart
  runApp(DesignLanguageDispatcher(
    material: (_, __) => const MyApp(), // selected by default by all platforms but iOS and macOS
    cupertino: (_, __) => const MyIosApp(), // selected by default by iOS
    macos: (_, __) => const MyMacosApp(), // selected by default by macOS
  ));
}

If the goal is to force a specific design language on startup for all platforms:

void main() {
  DesignLanguage.notifier =
      DesignLanguageNotifier(DesignLanguages.cupertino);
  // the design language is reset upon every restart
  runApp(DesignLanguageDispatcher(
    material: (_, __) => const MyApp(),
    cupertino: (_, __) => const MyIosApp(), // all platforms will select this by default
    macos: (_, __) => const MyMacosApp(),
  ));
}

It is also possible to load at startup the design language that had been saved to persistance storage in a previous session.

// in a previous session:
sharedPreferences.setString('design_language', DesignLanguage.current.toString());

// when the app starts:
Future<void> main() async {
  final sharedPreferences = await SharedPreferences.getInstance();
  final previous = sharedPreferences.getString('design_language');
  final previousDesignLanguage = DesignLanguage.fromString(previous); // if null or invalid, a platform-specific fallback value will be chosen
  DesignLanguage.notifier = DesignLanguageNotifier(previousDesignLanguage);
  runApp(DesignLanguageDispatcher(
    material: (_, __) => const MyApp(),
    cupertino: (_, __) => const MyIosApp(),
    macos: (_, __) => const MyMacosApp(),
  ));
}

How to change the design language

You can change the platform by accessing DesignLanguage's setter chosenDesignLanguage, e.g.: `

ElevatedButton(
    onPressed: () => DesignLanguage.notifier.chosenDesignLanguage =
        DesignLanguages.material,
    child: const Text('set material'),
),

Or you can also reset this setting.

ElevatedButton(
    onPressed: () => DesignLanguage.notifier.chosenDesignLanguage = 
        DesignLanguage.fromPhysicalPlatform,
    child: const Text('reset'),
),

DesignLanguages

DesignLanguages is a class containing all possible DesignLanguage values as static fields. The constructors of DesignLanguages and DesignLanguage are both private.

N.B.: The default and current design languages are accessed through DesignLanguage (and not through DesignLanguages):

  • DesignLanguage.fromPhysicalPlatform
  • DesignLanguage.current (once the notifier is set)

DesignLanguageDispatcher #

DesignLanguageDispatcher is used for dispatching a widget based on the design language.

DesignLanguageDispatcher(
  material: (_, __) => const MyApp(child: const CommonSubtree()),
  cupertino: (_, __) => const MyIosApp(child: const CommonSubtree()),
  macos: (_, __) => const MyMacosApp(child: const CommonSubtree()),
);

Optional child

It is possible to pass a child avoid repeating it in all builders. The child will not get rebuilt when the design language changes. The child should not be a widget of the Material or Cupertino family, but a design-language neutral one. The child can nest DesignLanguageDispatchers.

Rewritten example above:

DesignLanguageDispatcher(
  child: const CommonSubtree(), // inside it there might be other widgets using DesignLanguageDispatcher
  material: (_, child) => const MyApp(child: child),
  cupertino: (_, child) => const MyIosApp(child: child),
  macos: (_, child) => const MyMacosApp(child: child),
);

matchDesignLanguage #

You can invoke matchDesignLanguage in case you need to invoke some design-language-specific functions, e.g.:

MyImage( // widget used under all design languages (in our case material, cupertino and macos)
  ...
  onPressed: () {
    matchDesignLanguage(
      cupertino: () => showCupertinoModalPopup(...),
      material: () => showModalBottomSheet(...),
      macos: () => showMacosModalPopup(...),
    );
  },
)

Overrides #

If you don't like the default values this package assigns, feel free to override them with DesignLanguage.overrides:

DesignLanguage.overrides = {"linux": DesignLanguages.macos, "ios": DesignLanguages.material};

Add this code to your main function.

Why is the library creation step necessary? #

This step is necessary to prevent exposing too much code that can only get in the way. If there is only one library exposed, the user can be assured that every time the IDE autocompletion assistant suggests the intended DesignLanguageDispatcher, matchDesignLanguage, ...

Opposite case #

Imagine the opposite case:

Pre-made libraries:

  • package:design_language/material_cupertino.dart
  • package:design_language/material_cupertino_macos.dart
  • package:design_language/material_cupertino_macos_fluent.dart

Utils having the same names

Input: DesignLanguageDi

Suggested options:

  • DesignLanguageDispatcher from package:design_language/material_cupertino.dart
  • DesignLanguageDispatcher from package:design_language/material_cupertino_macos.dart
  • DesignLanguageDispatcher from package:design_language/material_cupertino_macos_fluent.dart

If you choose the wrong one, then there will be a group mismatch. If you don't notice it, then there will be a runtime crash, because the other group's notifier was not instantiated. Thus, this option isn't satisfying.

Utils having different names

Input: MC

Suggested options:

  • MCDispatcher from package:design_language/material_cupertino.dart
  • MCMDispatcher from package:design_language/material_cupertino_macos.dart
  • MCMFDispatcher from package:design_language/material_cupertino_macos_fluent.dart

Now would be slightly harder to make a mistake, however, there is no need to display MCDispatcher and MCMFDispatcher if the app is supposed to support exactly three design languages (i.e., the app must support all of these widgets MaterialApp, CupertinoApp, MacosApp, and must not support Fluent).

Let's say we fix the notifier problem described above. There would still be issues with this approach:

  • MCDispatcher: a macos chain is missing everywhere. If the device uses MacosApp in the widget three, there will be a crash.
  • MCMFDispatcher: a fluent chain is needed too, however, it just can't be reached.

Thus, this option also isn't satisfying.

Design language deprecation or addition #

Another reason for favoring this self-declared library creation step is that if you simply change the export, e.g., from

library design_language;
export 'package:design_language/src/material_cupertino_macos/library.dart';

to:

library design_language;
export 'package:design_language/src/material_cupertino/library.dart';

then no imports will need to be changed in the rest of your application, i.e., they will all stay the same, e.g.:

import 'package:example_app/core/design/design_language.dart';

and at the same time, the IDE will show you errors and force you to remove or add all affected design language callbacks (in the case above, all macos callbacks will have to be removed).

In short, a self-defined library is an elegant solution to the pluri-design-language problem.

2
likes
0
pub points
0%
popularity

Publisher

verified publishermanuelplavsic.ch

A package that makes cross-design-language development a breeze.

Repository (GitLab)
View/report issues

License

unknown (license)

Dependencies

flutter

More

Packages that depend on design_language