Skip to content

Commit

Permalink
[adaptive_style] add first implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Francesco Iapicca committed Apr 23, 2024
1 parent d2ca9d2 commit 0539209
Show file tree
Hide file tree
Showing 9 changed files with 234 additions and 10 deletions.
7 changes: 7 additions & 0 deletions packages/adaptive_style/lib/adaptive_style.dart
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
library adaptive_style;

export 'src/adaptive_size_builder.dart';
export 'src/adaptive_size_provider.dart';
export 'src/adaptive_size.dart';
export 'src/device_size.dart';
export 'src/extension.dart';
export 'src/inherited_adaptive_size.dart';
44 changes: 44 additions & 0 deletions packages/adaptive_style/lib/src/adaptive_size.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import 'package:flutter/widgets.dart';
import 'package:yak_flutter/yak_flutter.dart';
import 'extension.dart';
import 'device_size.dart';

typedef AdaptiveSizeNotifier = RestrictedNotifier<AdaptiveSizeData>;

final class AdaptiveSizeData {
const AdaptiveSizeData({
required this.realScreenSize,
required this.scaledSize,
required this.scale,
required this.deviceSizes,
required this.mostSimilarDeviceSize,
});

factory AdaptiveSizeData.fromSize(
Size size, {
List<DeviceSize> deviceSizes = DeviceSize.values,
}) {
final mostSimilarDeviceSize = deviceSizes.mostSimilarTo(size);
final scale = mostSimilarDeviceSize.size.closestDimentionScale(size);
return AdaptiveSizeData(
deviceSizes: deviceSizes,
realScreenSize: size,
mostSimilarDeviceSize: mostSimilarDeviceSize,
scale: scale,
scaledSize: size * scale,
);
}

final Size realScreenSize;
final Size scaledSize;
final double scale;
final List<DeviceSize> deviceSizes;
final DeviceSize mostSimilarDeviceSize;

@override
bool operator ==(Object other) =>
other is AdaptiveSizeData && other.hashCode == hashCode;

@override
int get hashCode => Object.hash(realScreenSize, deviceSizes);
}
27 changes: 27 additions & 0 deletions packages/adaptive_style/lib/src/adaptive_size_builder.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'package:flutter/widgets.dart';

import 'adaptive_size.dart';
import 'inherited_adaptive_size.dart';

typedef AdaptiveSizeBuild = Widget Function(BuildContext, AdaptiveSizeData);

class AdaptiveSizeBuilder extends StatelessWidget {
final AdaptiveSizeBuild builder;
const AdaptiveSizeBuilder({
super.key,
required this.builder,
});

@override
Widget build(context) {
final adaptiveSize = InheritedAdaptiveSize.maybeOf(context);
if (adaptiveSize == null) {
throw Exception('InheritedAdaptiveSize not found in BuildContext');
}

return ValueListenableBuilder(
valueListenable: adaptiveSize as ValueNotifier<AdaptiveSizeData>,
builder: (context, value, _) => builder(context, value),
);
}
}
70 changes: 70 additions & 0 deletions packages/adaptive_style/lib/src/adaptive_size_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import 'dart:async';

import 'package:flutter/widgets.dart';
import 'package:yak_flutter/yak_flutter.dart';

import 'adaptive_size.dart';
import 'device_size.dart';
import 'inherited_adaptive_size.dart';

class AdaptiveSizeProvider extends StatefulWidget {
final Widget child;
final List<DeviceSize> deviceSizes;
const AdaptiveSizeProvider({
super.key,
required this.child,
this.deviceSizes = DeviceSize.values,
});

@override
State<AdaptiveSizeProvider> createState() => _AdaptiveSizeProvidertState();
}

class _AdaptiveSizeProvidertState extends State<AdaptiveSizeProvider> {
final _initialized = Completer<bool>();
late final ValueNotifier<AdaptiveSizeData> _notifier;

@override
void didChangeDependencies() {
if (!_initialized.isCompleted) {
_initialize(context);
return;
}
_update(context);
super.didChangeDependencies();
}

@override
void dispose() {
_notifier.dispose();
super.dispose();
}

void _update(BuildContext context) {
final size = MediaQuery.sizeOf(context);
if (size == _notifier.value.realScreenSize &&
widget.deviceSizes == _notifier.value.deviceSizes) {
return;
}

_notifier.value = AdaptiveSizeData.fromSize(
size,
deviceSizes: widget.deviceSizes,
);
}

void _initialize(BuildContext size) {
final adaptiveSize = AdaptiveSizeData.fromSize(
MediaQuery.sizeOf(context),
deviceSizes: widget.deviceSizes,
);
_notifier = ValueNotifier<AdaptiveSizeData>(adaptiveSize);
_initialized.complete(true);
}

@override
Widget build(context) => InheritedAdaptiveSize(
notifier: RestrictedNotifier(_notifier),
child: widget.child,
);
}
2 changes: 1 addition & 1 deletion packages/adaptive_style/lib/src/device_size.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'dart:ui';
import 'dart:ui' show Size;

/// a collection of well known screen dimesions
/// as offered by [chrome device toolbar]
Expand Down
56 changes: 47 additions & 9 deletions packages/adaptive_style/lib/src/extension.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import 'dart:math' as math show min, sqrt;
import 'dart:ui';
import 'dart:ui' show Size;

import 'device_size.dart';

extension DeviceSizeSizesX on List<DeviceSize> {
List<Size> get sizes => [for (final value in this) value.size];
}

extension FixedRatioSizeScaleX<T extends Size> on T {
double fixedRatioScale(T size) => math.min(
extension ClosestDimentionScaleX<T extends Size> on T {
double closestDimentionScale(T size) => math.min(
width / size.width,
height / size.height,
);
Expand All @@ -22,16 +22,16 @@ extension SizeAreaX<T extends Size> on T {
double get area => width * height;
}

extension MostSimilarSizeX<T extends Size> on Iterable<T> {
T mostSimilarTo(T size) {
extension MostSimilarSizeX<T extends Size> on Iterable<DeviceSize> {
DeviceSize mostSimilarTo(T size) {
final sizes = [
for (final element in this)
for (final deviceSize in this)
(
element,
deviceSize,

/// this is arbitrary but seems to work
(size.aspectRatio - element.aspectRatio).abs() *
math.sqrt((size.area - element.area).abs())
(size.aspectRatio - deviceSize.size.aspectRatio).abs() *
math.sqrt((size.area - deviceSize.size.area).abs())
)
]..sort(
(a, b) => a.$2.compareTo(b.$2),
Expand All @@ -40,3 +40,41 @@ extension MostSimilarSizeX<T extends Size> on Iterable<T> {
return sizes.first.$1;
}
}

extension DeviceSizeAverageSizeX on List<DeviceSize> {
Size get averagePortrait {
final portraits = [
for (final device in this)
if (device.size.aspectRatio < 1) device.size,
];
var height = .0;
var width = .0;
for (final portrait in portraits) {
width += portrait.width;
height += portrait.height;
}

return Size(
width / portraits.length,
height / portraits.length,
);
}

Size get averageLandscape {
final landscapes = [
for (final device in this)
if (device.size.aspectRatio > 1) device.size,
];
var height = .0;
var width = .0;
for (final landscape in landscapes) {
width += landscape.width;
height += landscape.height;
}

return Size(
width / landscapes.length,
height / landscapes.length,
);
}
}
24 changes: 24 additions & 0 deletions packages/adaptive_style/lib/src/inherited_adaptive_size.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import 'package:flutter/widgets.dart';
import 'package:yak_flutter/yak_flutter.dart';

import 'adaptive_size.dart';

class InheritedAdaptiveSize
extends InheritedRestrictedNotifier<AdaptiveSizeData> {
const InheritedAdaptiveSize({
super.key,
required super.notifier,
required super.child,
});

static AdaptiveSizeNotifier? maybeOf(BuildContext context) {
final inherited =
context.dependOnInheritedWidgetOfExactType<InheritedAdaptiveSize>();

if (inherited == null) {
return null;
}

return inherited.notifier;
}
}
2 changes: 2 additions & 0 deletions packages/adaptive_style/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ environment:
dependencies:
flutter:
sdk: flutter
yak_flutter: ^3.0.2

dev_dependencies:
flutter_test:
sdk: flutter
Expand Down
12 changes: 12 additions & 0 deletions packages/adaptive_style/pubspec_overrides.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# melos_managed_dependency_overrides: yak_flutter,yak_result,yak_runner,yak_tween,yak_utils
dependency_overrides:
yak_flutter:
path: ../yak_flutter
yak_result:
path: ../yak_result
yak_runner:
path: ../yak_runner
yak_tween:
path: ../yak_tween
yak_utils:
path: ../yak_utils

0 comments on commit 0539209

Please sign in to comment.