mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
This PR tweaks `wrapWithDefaultView` (used by `runApp`) to raise a StateError with a legible error message when the `platformDispatcher.implicitView` is missing (for example, when enabling multi-view embedding on the web), instead of crashing with an unexpected `nullCheck` a few lines below.
* Before:
<img width="619" alt="Screenshot 2024-09-25 at 7 33 47â¯PM" src="https://github.com/user-attachments/assets/4897dd3c-bdd0-4217-9f23-7eee9fab4999">
* After:
<img width="613" alt="Screenshot 2024-09-26 at 5 01 49â¯PM" src="https://github.com/user-attachments/assets/3febb91d-a8c3-41b6-bf34-c2c8743b637c">
## Issues
* Fixes https://github.com/flutter/flutter/issues/153198
## Tests
Added a test to ensure the assertion is thrown when the `implicitView` is missing. Had to hack a little because I couldn't find any clean way of overriding the `implicitView`. The problem is that the flutter_test bindings [use `runApp` internally](8925e1ffdf/packages/flutter_test/lib/src/binding.dart (L1020)) a couple of times, so I can only disable the implicitView inside the test body (and must re-enable it before returning). Not sure if it's the best way, but it seems to do the trick for this simple test case!
79 lines
2.9 KiB
Dart
79 lines
2.9 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'dart:ui';
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
class FakeView extends TestFlutterView {
|
|
FakeView(FlutterView view, { this.viewId = 100 }) : super(
|
|
view: view,
|
|
platformDispatcher: view.platformDispatcher as TestPlatformDispatcher,
|
|
display: view.display as TestDisplay,
|
|
);
|
|
|
|
@override
|
|
final int viewId;
|
|
|
|
@override
|
|
void render(Scene scene, {Size? size}) {
|
|
// Do not render the scene in the engine. The engine only observes one
|
|
// instance of FlutterView (the _view), and it is generally expected that
|
|
// the framework will render no more than one `Scene` per frame.
|
|
}
|
|
|
|
@override
|
|
void updateSemantics(SemanticsUpdate update) {
|
|
// Do not send the update to the engine. The engine only observes one
|
|
// instance of FlutterView (the _view). Sending semantic updates meant for
|
|
// different views to the same engine view does not work as the updates do
|
|
// not produce consistent semantics trees.
|
|
}
|
|
}
|
|
|
|
/// A test platform dispatcher that can show/hide its underlying `implicitView`,
|
|
/// depending on the value of the [implicitViewHidden] flag.
|
|
class NoImplicitViewPlatformDispatcher extends TestPlatformDispatcher {
|
|
NoImplicitViewPlatformDispatcher({ required super.platformDispatcher }) : superPlatformDispatcher = platformDispatcher;
|
|
|
|
final PlatformDispatcher superPlatformDispatcher;
|
|
|
|
bool implicitViewHidden = false;
|
|
|
|
@override
|
|
TestFlutterView? get implicitView {
|
|
return implicitViewHidden
|
|
? null
|
|
: superPlatformDispatcher.implicitView as TestFlutterView?;
|
|
}
|
|
}
|
|
|
|
/// Test Flutter Bindings that allow tests to hide/show the `implicitView`
|
|
/// of their [NoImplicitViewPlatformDispatcher] `platformDispatcher`.
|
|
///
|
|
/// This is used to test that [runApp] throws an assertion error with an
|
|
/// explanation when used when the `implicitView` is disabled (like in Flutter
|
|
/// web when multi-view is enabled).
|
|
///
|
|
/// Because of how [testWidgets] uses `runApp` internally to manage the lifecycle
|
|
/// of a test, the implicitView must be disabled/reenabled inside of the body of
|
|
/// the [WidgetTesterCallback] under test. In practice: the implicitView is disabled
|
|
/// in the first line of the test, and reenabled in the last.
|
|
///
|
|
/// See: multi_view_no_implicitView_binding_test.dart
|
|
class NoImplicitViewWidgetsBinding extends AutomatedTestWidgetsFlutterBinding {
|
|
late final NoImplicitViewPlatformDispatcher _platformDispatcher = NoImplicitViewPlatformDispatcher(platformDispatcher: super.platformDispatcher);
|
|
|
|
@override
|
|
NoImplicitViewPlatformDispatcher get platformDispatcher => _platformDispatcher;
|
|
|
|
void hideImplicitView() {
|
|
platformDispatcher.implicitViewHidden = true;
|
|
}
|
|
|
|
void showImplicitView() {
|
|
platformDispatcher.implicitViewHidden = false;
|
|
}
|
|
}
|