physical_platform 0.3.1 physical_platform: ^0.3.1 copied to clipboard
A Flutter package that simplifies the cross-platform development process by providing declarative and pragmatic instruments.
Welcome to physical_platform
, a package that provides pragmatic physical-platform utils.
NB: If you are using also virtual_platform
, you can opt to only use omni_platform
instead. omni_platform
integrates virtual_platform
and physical_platform
, without adding any extra functionality. This way you only need to declare one package in your pubspec.yaml
.
Warning: Prefer virtual_platform
or design_language
wherever possible
In order to increase testability, my recommendation is to rely first on either:
- a virtual platform matcher/dispatcher
- it is used in case the design language does not change at runtime (usually that means only one design language is used); consult the
virtual_platform
package for more information.
- it is used in case the design language does not change at runtime (usually that means only one design language is used); consult the
- a design language matcher/dispatcher
- it is used in case the design language changes at runtime; consult the
design_language
package for more information.
- it is used in case the design language changes at runtime; consult the
In certain cases, however, it is not possible to use the packages above; that is when this package becomes very handy.
Getting started #
You need to add physical_platform
to your dependencies.
dependencies:
physical_platform: ^latest # replace latest with version number
Next, you have to import package:physical_platform/physical_platform.dart
.
Usage #
Many parts of code only depend on the physical platform. This package offers expression-based, pragmatic physical-platform utils, allowing developers to write more declarative code compared to typical Dart code (see examples below). It has a syntax similar to the ony of virtual_platform
.
The arguments of all instruments are functions, i.e., PhysicalPlatformDispatcher
and matchPhysicalPlatform
are of
type Widget Function(BuildContext context, Widget? child)
and T Function()
, respectively. If the goal is to only select a widget or a value — a function
expression is what needs to be passed, e.g., (_, __) => Text('text')
or () => 'value'
, respectively.
Platforms and platform groups are summarized at the end of this README.
PhysicalPlatformDispatcher #
This dispatcher relies on the physical platform.
It builds a widget, and it is used when a widget is not supported only by all platforms.
For example, as of 2022 the flutter_webview
plugin does not support the desktop OSs yet:
PhysicalPlatformDispatcher(
android: (_, __) => MyWebView(),
ios: (_, __) => MyWebView(),
other: (_, __) => Center(child: Text('platform not supported')),
);
or also:
PhysicalPlatformDispatcher(
mobileSystems: (_, __) => MyWebView(),
other: (_, __) => Center(child: Text('platform not supported')),
);
Platforms and platform groups are summarized at the end of this README.
Context and child provision
One might need the context in some cases. The first parameter of all builders is a BuildContext context
. You can also specify a Widget? child
widget, so that you do not have to repeat it in all builders.
Example:
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(title: const Text('example')),
body: PhysicalPlatformDispatcher(
child: const CommonSubtree(),
mobileSystems: (context, child) => SpecialWidget(
child,
onPressed(() {do something with context}),
),
other: (context, child) => OtherSpecialWidget(
child,
onPressed(() {do something with context}),
),
);
),
);
matchPhysicalPlatform #
matchPhysicalPlatform
is a declarative pattern to invoke the right function for
the matching physical platform. It uses generics.
Usage example:
// one should use path_provider here to do it correctly
final dbPath = matchPhysicalPlatform(
other: () => 'default/path/to/db',
android: () => 'path/to/db/on/android',
macos: () => 'path/to/db/on/android',
);
dbPath
will have value 'default/path/to/db'
on all platforms (including web), except on Android and macOS.
This function will probably be the instrument you are going to use the most from the physical_platform
package.
How is this solution more pragmatic than typical Dart?
Example 1
This package:
matchPhysicalPlatform(
mobileSystems: () {...},
desktopSystems: () {...},
web: () {...},
other: () {...},
);
Typical Dart:
if (kIsWeb) { // this should always go first
...
} else if (Platform.isIOS || Platform.isAndroid) {
...
} else if (Platform.isLinux || Platform.isWindows || Platform.isMacOS) {
...
} else {
...
}
Example 2
This package:
matchPhysicalPlatform(
android: () {...},
ios: () {...},
web: () {...},
other: () {...},
);
Typical Dart:
if (kIsWeb) {
...
} else if (Platform.isIOS) {
...
} else if (Platform.isAndroid) {
...
} else {
...
}
Platforms and platform groups #
Platform | appleSystems | mobileSystems | desktopSystems |
---|---|---|---|
android | ✓ | ||
ios | ✓ | ✓ | |
linux | ✓ | ||
macos | ✓ | ✓ | |
windows | ✓ | ||
web | |||
fuchsia |
Priority order: from left to right.