Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
[url_launcher] Remove renderView usage
Removes calls to the deprecated `renderView` method, replacing them with
best-effort lookup of the implicit view. This only affects an API that
has been deprecated for almost two years, and only when using a specific
optional parameter on that method, so the potential impact here is
minimal, and this avoids the need for a breaking change.

In the future, when we remove this deprecated API, the workaround will
go away as well.

Fixes flutter/flutter#143449
  • Loading branch information
stuartmorgan-g committed Feb 15, 2024
commit 608ac110e745f183684f9ba87365604dba5827af
49 changes: 31 additions & 18 deletions packages/url_launcher/url_launcher/lib/src/legacy_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
// found in the LICENSE file.

import 'dart:async';
import 'dart:ui';

import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
Expand Down Expand Up @@ -85,15 +87,14 @@ Future<bool> launch(

/// [true] so that ui is automatically computed if [statusBarBrightness] is set.
bool previousAutomaticSystemUiAdjustment = true;
if (statusBarBrightness != null &&
defaultTargetPlatform == TargetPlatform.iOS &&
_ambiguate(WidgetsBinding.instance) != null) {
previousAutomaticSystemUiAdjustment = _ambiguate(WidgetsBinding.instance)!
.renderView
.automaticSystemUiAdjustment;
_ambiguate(WidgetsBinding.instance)!
.renderView
.automaticSystemUiAdjustment = false;
final RenderView? renderViewToAdjust =
statusBarBrightness != null && defaultTargetPlatform == TargetPlatform.iOS
? _findImplicitRenderView()
: null;
if (renderViewToAdjust != null) {
previousAutomaticSystemUiAdjustment =
renderViewToAdjust.automaticSystemUiAdjustment;
renderViewToAdjust.automaticSystemUiAdjustment = false;
SystemChrome.setSystemUIOverlayStyle(statusBarBrightness == Brightness.light
? SystemUiOverlayStyle.dark
: SystemUiOverlayStyle.light);
Expand All @@ -110,11 +111,9 @@ Future<bool> launch(
webOnlyWindowName: webOnlyWindowName,
);

if (statusBarBrightness != null &&
_ambiguate(WidgetsBinding.instance) != null) {
_ambiguate(WidgetsBinding.instance)!
.renderView
.automaticSystemUiAdjustment = previousAutomaticSystemUiAdjustment;
if (renderViewToAdjust != null) {
renderViewToAdjust.automaticSystemUiAdjustment =
previousAutomaticSystemUiAdjustment;
}

return result;
Expand Down Expand Up @@ -146,8 +145,22 @@ Future<void> closeWebView() async {
return UrlLauncherPlatform.instance.closeWebView();
}

/// This allows a value of type T or T? to be treated as a value of type T?.
/// Returns the [RenderView] associated with the implicit [FlutterView], if any.
///
/// We use this so that APIs that have become non-nullable can still be used
/// with `!` and `?` on the stable branch.
T? _ambiguate<T>(T? value) => value;
/// [launch] predates multi-window support, and it doesn't have enough context
/// to get the right render view, so this assumes anyone still trying to use
/// the deprecated API with `statusBarBrightness` is in a single-view scenario.
/// This allows a best-effort implementation of the deprecated API for as long
/// as it continues to exist, without depending on deprecated Flutter APIs (and
/// therefore keeping url_launcher forward-compatible with future versions of
/// Flutter for longer).
RenderView? _findImplicitRenderView() {
final FlutterView? implicitFlutteView =
WidgetsBinding.instance.platformDispatcher.implicitView;
if (implicitFlutteView == null) {
return null;
}
return WidgetsBinding.instance.renderViews
.where((RenderView v) => v.flutterView == implicitFlutteView)
.firstOrNull;
}
22 changes: 10 additions & 12 deletions packages/url_launcher/url_launcher/test/src/legacy_api_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -235,10 +235,11 @@ void main() {
..setResponse(true);

final TestWidgetsFlutterBinding binding =
_anonymize(TestWidgetsFlutterBinding.ensureInitialized())!
as TestWidgetsFlutterBinding;
TestWidgetsFlutterBinding.ensureInitialized();
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
final RenderView renderView = binding.renderView;
final RenderView renderView =
RenderView(view: binding.platformDispatcher.implicitView!);
binding.addRenderView(renderView);
renderView.automaticSystemUiAdjustment = true;
final Future<bool> launchResult =
launch('http://flutter.dev/', statusBarBrightness: Brightness.dark);
Expand All @@ -248,6 +249,7 @@ void main() {
expect(renderView.automaticSystemUiAdjustment, isFalse);
await launchResult;
expect(renderView.automaticSystemUiAdjustment, isTrue);
binding.removeRenderView(renderView);
});

test('sets automaticSystemUiAdjustment to not be null', () async {
Expand All @@ -265,10 +267,11 @@ void main() {
..setResponse(true);

final TestWidgetsFlutterBinding binding =
_anonymize(TestWidgetsFlutterBinding.ensureInitialized())!
as TestWidgetsFlutterBinding;
TestWidgetsFlutterBinding.ensureInitialized();
debugDefaultTargetPlatformOverride = TargetPlatform.android;
final RenderView renderView = binding.renderView;
final RenderView renderView =
RenderView(view: binding.platformDispatcher.implicitView!);
binding.addRenderView(renderView);
expect(renderView.automaticSystemUiAdjustment, true);
final Future<bool> launchResult =
launch('http://flutter.dev/', statusBarBrightness: Brightness.dark);
Expand All @@ -278,6 +281,7 @@ void main() {
expect(renderView.automaticSystemUiAdjustment, true);
await launchResult;
expect(renderView.automaticSystemUiAdjustment, true);
binding.removeRenderView(renderView);
});

test('open non-parseable url', () async {
Expand Down Expand Up @@ -317,9 +321,3 @@ void main() {
});
});
}

/// This removes the type information from a value so that it can be cast
/// to another type even if that cast is redundant.
/// We use this so that APIs whose type have become more descriptive can still
/// be used on the stable branch where they require a cast.
Object? _anonymize<T>(T? value) => value;