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

feat: Add PlatformWidgetBuilder. #319

Merged
merged 9 commits into from
Mar 21, 2024
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export 'platform/catalyst_platform.dart';
export 'platform_aware_builder/platform_aware_builder.dart';
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'dart:io';

import 'package:catalyst_voices_shared/src/platform/platform_key.dart';

final class CatalystPlatform {
static bool get isAndroid => Platform.isAndroid;

Expand All @@ -23,4 +25,18 @@ final class CatalystPlatform {
static bool get isWebDesktop => false;

static bool get isWindows => Platform.isWindows;

static Map<PlatformKey, bool> get identifiers => {
PlatformKey.android: isAndroid,
PlatformKey.desktop: isDesktop,
PlatformKey.fuchsia: isFuchsia,
PlatformKey.iOS: isIOS,
PlatformKey.linux: isLinux,
PlatformKey.macOS: isMacOS,
PlatformKey.mobile: isMobile,
PlatformKey.mobileWeb: isMobileWeb,
PlatformKey.web: isWeb,
PlatformKey.webDesktop: isWebDesktop,
PlatformKey.windows: isWindows,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
enum PlatformKey {
android,
desktop,
fuchsia,
iOS,
linux,
macOS,
mobile,
mobileWeb,
web,
webDesktop,
windows,
other,
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:catalyst_voices_shared/src/platform/platform_key.dart';

final class CatalystPlatform {
static bool get isAndroid {
throw UnimplementedError('Stub CatalystPlatform');
Expand Down Expand Up @@ -43,5 +45,9 @@ final class CatalystPlatform {
throw UnimplementedError('Stub CatalystPlatform');
}

static Map<PlatformKey, bool> get identifiers {
throw UnimplementedError('Stub CatalystPlatform');
}

const CatalystPlatform._();
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:catalyst_voices_shared/src/platform/platform_key.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:web/web.dart';

Expand Down Expand Up @@ -33,6 +34,20 @@ final class CatalystPlatform {
];
return mobileIdentifiers.any(userAgent.contains);
}

static Map<PlatformKey, bool> get identifiers => {
PlatformKey.android: isAndroid,
PlatformKey.desktop: isDesktop,
PlatformKey.fuchsia: isFuchsia,
PlatformKey.iOS: isIOS,
PlatformKey.linux: isLinux,
PlatformKey.macOS: isMacOS,
PlatformKey.mobile: isMobile,
PlatformKey.mobileWeb: isMobileWeb,
PlatformKey.web: isWeb,
PlatformKey.webDesktop: isWebDesktop,
PlatformKey.windows: isWindows,
};

const CatalystPlatform._();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import 'package:catalyst_voices_shared/src/platform/catalyst_platform.dart';
import 'package:catalyst_voices_shared/src/platform/platform_key.dart';
import 'package:flutter/widgets.dart';

// A [PlatformAwareBuilder] is a StatelessWidget that is aware of the current
// platform.
//
// This is an abstract widget that has a required argument [builder] that can
// consume platform-specific data automatically based on platform that is
// detected.
//
// The platform detection happens in [CatalystPlatform].
//
// The widget accepts an argument for each specific platform defined in
// [PlatformKey]. The platform specific [data] is selected when two conditions
// are verified at the same time:
// - the platform is detected
// - the platform-specific argument is present
// In case those conditions are not verified the [other] argument is used (and
// because of this it is required).
// The type of the platform specific data is generic.
//
// A simple usage is to render a string based on the platform:
//
// ```dart
// PlatformWidgetBuilder<String>(
// android: 'This is an Android platform.',
// other: 'This is an other platform.',
// builder: (context, title) => Text(title!),
// );
// ```
//
// or to have a specific Padding:
//
// ```dart
// PlatformWidgetBuilder<EdgeInsetsGeometry>(
// android: EdgeInsets.all(10.0),
// other: EdgeInsets.all(40.0),
// builder: (context, padding) => Padding(
// padding: padding,
// child: Text('This is an example.')
// ),
// );
// ```

class PlatformAwareBuilder<T> extends StatelessWidget {
final Widget Function(BuildContext context, T? data) builder;
final Map<PlatformKey, T?> _platformData;

PlatformAwareBuilder({
super.key,
required this.builder,
T? android,
T? desktop,
T? fuchsia,
T? iOS,
T? linux,
T? macOS,
T? mobile,
T? mobileWeb,
T? web,
T? webDesktop,
T? windows,
required T other,
}) : _platformData = {
PlatformKey.android: android,
PlatformKey.desktop: desktop,
PlatformKey.fuchsia: fuchsia,
PlatformKey.iOS: iOS,
PlatformKey.linux: linux,
PlatformKey.macOS: macOS,
PlatformKey.mobile: mobile,
PlatformKey.mobileWeb: mobileWeb,
PlatformKey.web: web,
PlatformKey.webDesktop: webDesktop,
PlatformKey.windows: windows,
PlatformKey.other: other,
};

@override
Widget build(BuildContext context) {
return builder(context, _getPlatformData());
}

T _getPlatformData() {
final currentPlatformKey = CatalystPlatform.identifiers.entries
.firstWhere(
// We select the platform only if the platform-specific data
// is also present.
(entry) => entry.value && (_platformData[entry.key] != null),
orElse: () => const MapEntry(PlatformKey.other, true),
)
.key;
return _platformData[currentPlatformKey]!;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ dependencies:
dev_dependencies:
catalyst_analysis:
path: ../../../catalyst_voices_packages/catalyst_analysis
flutter_test:
sdk: flutter
test: ^1.24.9

Original file line number Diff line number Diff line change
@@ -1 +1 @@
void main() {}
export 'platform_aware_builder/platform_aware_builder_test.dart';
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import 'package:catalyst_voices_shared/src/catalyst_voices_shared.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {

Widget buildApp() => MaterialApp(
home: Scaffold(
body: PlatformAwareBuilder<String>(
other: 'other',
builder: (context, platformData) => Text(platformData!),
),
),
);

group('Test platform detection', () {
testWidgets('PlatformWidgetBuilder fallbacks to other', (tester) async {
await tester.pumpWidget(buildApp());
// Verify the Widget renders properly
expect(find.byType(Text), findsOneWidget);
// Check the output contains the platform that was tested.
expect(find.text('other'), findsOneWidget);

});
});
}
Loading