• Michael Goderbauer's avatar
    Reapply "Dynamic view sizing" (#140165) (#140918) · 4534a24c
    Michael Goderbauer authored
    This reverts commit
    https://github.com/flutter/flutter/commit/d24c01bd0c41331bd17165e0173b24c5d05d7c0a.
    
    The original change was reverted because it caused some apps to get
    stuck on the splash screen on some phones.
    
    An investigation determined that this was due to a rounding error.
    Example: The device reports a physical size of 1008.0 x 2198.0 with a
    dpr of 1.912500023841858. Flutter would translate that to a logical size
    of 527.0588169589221 x 1149.2810314243163 and use that as the input for
    its layout algorithm. Since the constraints here are tight, the layout
    algorithm would determine that the resulting logical size of the root
    render object must be 527.0588169589221 x 1149.2810314243163.
    Translating this back to physical pixels by applying the dpr resulted in
    a physical size of 1007.9999999999999 x 2198.0 for the frame. Android
    now rejected that frame because it didn't match the expected size of
    1008.0 x 2198.0 and since no frame had been rendered would never take
    down the splash screen.
    
    Prior to dynamically sized views, this wasn't an issue because we would
    hard-code the frame size to whatever the requested size was.
    
    Changes in this PR over the original PR:
    
    * The issue has been fixed now by constraining the calculated physical
    size to the input physical constraints which makes sure that we always
    end up with a size that is acceptable to the operating system.
    * The `ViewConfiguration` was refactored to use the slightly more
    convenient `BoxConstraints` over the `ViewConstraints` to represent
    constraints. Both essentially represent the same thing, but
    `BoxConstraints` are more powerful and we avoid a couple of translations
    between the two by translating the` ViewConstraints` from the
    `FlutterView` to `BoxConstraints` directly when the `ViewConfiguration`
    is created.
    
    All changes over the original PR are contained in the second commit of
    this PR.
    
    Fixes b/316813075
    Part of https://github.com/flutter/flutter/issues/134501.
    4534a24c
box_constraints_test.dart 6.03 KB
// 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/rendering.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  test('BoxConstraints toString', () {
    expect(const BoxConstraints.expand().toString(), contains('biggest'));
    expect(const BoxConstraints().toString(), contains('unconstrained'));
    expect(const BoxConstraints.tightFor(width: 50.0).toString(), contains('w=50'));
  });

  test('BoxConstraints copyWith', () {
    const BoxConstraints constraints = BoxConstraints(
      minWidth: 3.0,
      maxWidth: 7.0,
      minHeight: 11.0,
      maxHeight: 17.0,
    );
    BoxConstraints copy = constraints.copyWith();
    expect(copy, equals(constraints));
    copy = constraints.copyWith(
      minWidth: 13.0,
      maxWidth: 17.0,
      minHeight: 111.0,
      maxHeight: 117.0,
    );
    expect(copy.minWidth, 13.0);
    expect(copy.maxWidth, 17.0);
    expect(copy.minHeight, 111.0);
    expect(copy.maxHeight, 117.0);
    expect(copy, isNot(equals(constraints)));
    expect(copy.hashCode, isNot(equals(constraints.hashCode)));
  });

  test('BoxConstraints operators', () {
    const BoxConstraints constraints = BoxConstraints(
      minWidth: 3.0,
      maxWidth: 7.0,
      minHeight: 11.0,
      maxHeight: 17.0,
    );
    BoxConstraints copy = constraints * 2.0;
    expect(copy.minWidth, 6.0);
    expect(copy.maxWidth, 14.0);
    expect(copy.minHeight, 22.0);
    expect(copy.maxHeight, 34.0);
    expect(copy / 2.0, equals(constraints));
    copy = constraints ~/ 2.0;
    expect(copy.minWidth, 1.0);
    expect(copy.maxWidth, 3.0);
    expect(copy.minHeight, 5.0);
    expect(copy.maxHeight, 8.0);
    copy = constraints % 3.0;
    expect(copy.minWidth, 0.0);
    expect(copy.maxWidth, 1.0);
    expect(copy.minHeight, 2.0);
    expect(copy.maxHeight, 2.0);
  });

  test('BoxConstraints lerp', () {
    expect(BoxConstraints.lerp(null, null, 0.5), isNull);
    const BoxConstraints constraints = BoxConstraints(
      minWidth: 3.0,
      maxWidth: 7.0,
      minHeight: 11.0,
      maxHeight: 17.0,
    );
    BoxConstraints copy = BoxConstraints.lerp(null, constraints, 0.5)!;
    expect(copy.minWidth, moreOrLessEquals(1.5));
    expect(copy.maxWidth, moreOrLessEquals(3.5));
    expect(copy.minHeight, moreOrLessEquals(5.5));
    expect(copy.maxHeight, moreOrLessEquals(8.5));
    copy = BoxConstraints.lerp(constraints, null, 0.5)!;
    expect(copy.minWidth, moreOrLessEquals(1.5));
    expect(copy.maxWidth, moreOrLessEquals(3.5));
    expect(copy.minHeight, moreOrLessEquals(5.5));
    expect(copy.maxHeight, moreOrLessEquals(8.5));
    copy = BoxConstraints.lerp(const BoxConstraints(
      minWidth: 13.0,
      maxWidth: 17.0,
      minHeight: 111.0,
      maxHeight: 117.0,
    ), constraints, 0.2)!;
    expect(copy.minWidth, moreOrLessEquals(11.0));
    expect(copy.maxWidth, moreOrLessEquals(15.0));
    expect(copy.minHeight, moreOrLessEquals(91.0));
    expect(copy.maxHeight, moreOrLessEquals(97.0));
  });

  test('BoxConstraints.lerp identical a,b', () {
    expect(BoxConstraints.lerp(null, null, 0), null);
    const BoxConstraints constraints = BoxConstraints();
    expect(identical(BoxConstraints.lerp(constraints, constraints, 0.5), constraints), true);
  });

  test('BoxConstraints lerp with unbounded width', () {
    const BoxConstraints constraints1 = BoxConstraints(
      minWidth: double.infinity,
      minHeight: 10.0,
      maxHeight: 20.0,
    );
    const BoxConstraints constraints2 = BoxConstraints(
      minWidth: double.infinity,
      minHeight: 20.0,
      maxHeight: 30.0,
    );
    const BoxConstraints constraints3 = BoxConstraints(
      minWidth: double.infinity,
      minHeight: 15.0,
      maxHeight: 25.0,
    );
    expect(BoxConstraints.lerp(constraints1, constraints2, 0.5), constraints3);
  });

  test('BoxConstraints lerp with unbounded height', () {
    const BoxConstraints constraints1 = BoxConstraints(
      minWidth: 10.0,
      maxWidth: 20.0,
      minHeight: double.infinity,
    );
    const BoxConstraints constraints2 = BoxConstraints(
      minWidth: 20.0,
      maxWidth: 30.0,
      minHeight: double.infinity,
    );
    const BoxConstraints constraints3 = BoxConstraints(
      minWidth: 15.0,
      maxWidth: 25.0,
      minHeight: double.infinity,
    );
    expect(BoxConstraints.lerp(constraints1, constraints2, 0.5), constraints3);
  });

  test('BoxConstraints lerp from bounded to unbounded', () {
    const BoxConstraints constraints1 = BoxConstraints(
      minWidth: double.infinity,
      minHeight: double.infinity,
    );
    const BoxConstraints constraints2 = BoxConstraints(
      minWidth: 20.0,
      maxWidth: 30.0,
      minHeight: double.infinity,
    );
    const BoxConstraints constraints3 = BoxConstraints(
      minWidth: double.infinity,
      minHeight: 20.0,
      maxHeight: 30.0,
    );
    expect(() => BoxConstraints.lerp(constraints1, constraints2, 0.5), throwsAssertionError);
    expect(() => BoxConstraints.lerp(constraints1, constraints3, 0.5), throwsAssertionError);
    expect(() => BoxConstraints.lerp(constraints2, constraints3, 0.5), throwsAssertionError);
  });

  test('BoxConstraints normalize', () {
    const BoxConstraints constraints = BoxConstraints(
      minWidth: 3.0,
      maxWidth: 2.0,
      minHeight: 11.0,
      maxHeight: 18.0,
    );
    final BoxConstraints copy = constraints.normalize();
    expect(copy.minWidth, 3.0);
    expect(copy.maxWidth, 3.0);
    expect(copy.minHeight, 11.0);
    expect(copy.maxHeight, 18.0);
  });

  test('BoxConstraints.fromViewConstraints', () {
    final BoxConstraints unconstrained = BoxConstraints.fromViewConstraints(
      const ViewConstraints(),
    );
    expect(unconstrained, const BoxConstraints());

    final BoxConstraints constraints = BoxConstraints.fromViewConstraints(
      const ViewConstraints(minWidth: 1, maxWidth: 2, minHeight: 3, maxHeight: 4),
    );
    expect(constraints, const BoxConstraints(minWidth: 1, maxWidth: 2, minHeight: 3, maxHeight: 4));
  });

}