// 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 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { testWidgets('RenderParagraph relayout upon system fonts changes', (WidgetTester tester) async { await tester.pumpWidget( const MaterialApp( home: Text('text widget'), ), ); const Map<String, dynamic> data = <String, dynamic>{ 'type': 'fontsChange', }; await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage( 'flutter/system', SystemChannels.system.codec.encodeMessage(data), (ByteData data) { }, ); final RenderObject renderObject = tester.renderObject(find.text('text widget')); expect(renderObject.debugNeedsLayout, isTrue); }); testWidgets('RenderEditable relayout upon system fonts changes', (WidgetTester tester) async { await tester.pumpWidget( const MaterialApp( home: SelectableText('text widget'), ), ); const Map<String, dynamic> data = <String, dynamic>{ 'type': 'fontsChange', }; await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage( 'flutter/system', SystemChannels.system.codec.encodeMessage(data), (ByteData data) { }, ); final EditableTextState state = tester.state(find.byType(EditableText)); expect(state.renderEditable.debugNeedsLayout, isTrue); }); testWidgets('Banner repaint upon system fonts changes', (WidgetTester tester) async { await tester.pumpWidget( const Banner( message: 'message', location: BannerLocation.topStart, textDirection: TextDirection.ltr, layoutDirection: TextDirection.ltr, ), ); const Map<String, dynamic> data = <String, dynamic>{ 'type': 'fontsChange', }; await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage( 'flutter/system', SystemChannels.system.codec.encodeMessage(data), (ByteData data) { }, ); final RenderObject renderObject = tester.renderObject(find.byType(Banner)); expect(renderObject.debugNeedsPaint, isTrue); }); testWidgets('CupertinoDatePicker reset cache upon system fonts change - date time mode', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( home: CupertinoDatePicker( onDateTimeChanged: (DateTime dateTime) { }, ), ), ); final dynamic state = tester.state(find.byType(CupertinoDatePicker)); final Map<int, double> cache = state.estimatedColumnWidths as Map<int, double>; expect(cache.isNotEmpty, isTrue); const Map<String, dynamic> data = <String, dynamic>{ 'type': 'fontsChange', }; await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage( 'flutter/system', SystemChannels.system.codec.encodeMessage(data), (ByteData data) { }, ); // Cache should be cleaned. expect(cache.isEmpty, isTrue); final Element element = tester.element(find.byType(CupertinoDatePicker)); expect(element.dirty, isTrue); }, skip: isBrowser); // TODO(yjbanov): cupertino does not work on the Web yet: https://github.com/flutter/flutter/issues/41920 testWidgets('CupertinoDatePicker reset cache upon system fonts change - date mode', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( home: CupertinoDatePicker( mode: CupertinoDatePickerMode.date, onDateTimeChanged: (DateTime dateTime) { }, ), ), ); final dynamic state = tester.state(find.byType(CupertinoDatePicker)); final Map<int, double> cache = state.estimatedColumnWidths as Map<int, double>; // Simulates font missing. cache.clear(); const Map<String, dynamic> data = <String, dynamic>{ 'type': 'fontsChange', }; await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage( 'flutter/system', SystemChannels.system.codec.encodeMessage(data), (ByteData data) { }, ); // Cache should be replenished expect(cache.isNotEmpty, isTrue); final Element element = tester.element(find.byType(CupertinoDatePicker)); expect(element.dirty, isTrue); }, skip: isBrowser); // TODO(yjbanov): cupertino does not work on the Web yet: https://github.com/flutter/flutter/issues/41920 testWidgets('CupertinoDatePicker reset cache upon system fonts change - time mode', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( home: CupertinoTimerPicker( onTimerDurationChanged: (Duration d) { }, ), ), ); final dynamic state = tester.state(find.byType(CupertinoTimerPicker)); // Simulates wrong metrics due to font missing. state.numberLabelWidth = 0.0; state.numberLabelHeight = 0.0; state.numberLabelBaseline = 0.0; const Map<String, dynamic> data = <String, dynamic>{ 'type': 'fontsChange', }; await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage( 'flutter/system', SystemChannels.system.codec.encodeMessage(data), (ByteData data) { }, ); // Metrics should be refreshed expect(state.numberLabelWidth - 46.0 < precisionErrorTolerance, isTrue); expect(state.numberLabelHeight - 23.0 < precisionErrorTolerance, isTrue); expect(state.numberLabelBaseline - 18.400070190429688 < precisionErrorTolerance, isTrue); final Element element = tester.element(find.byType(CupertinoTimerPicker)); expect(element.dirty, isTrue); }, skip: isBrowser); // TODO(yjbanov): cupertino does not work on the Web yet: https://github.com/flutter/flutter/issues/41920 testWidgets('RangeSlider relayout upon system fonts changes', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( home: Material( child: RangeSlider( values: const RangeValues(0.0, 1.0), onChanged: (RangeValues values) { }, ), ), ), ); const Map<String, dynamic> data = <String, dynamic>{ 'type': 'fontsChange', }; await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage( 'flutter/system', SystemChannels.system.codec.encodeMessage(data), (ByteData data) { }, ); final RenderObject renderObject = tester.renderObject(find.byType(RangeSlider)); bool sliderBoxNeedsLayout; renderObject.visitChildren((RenderObject child) {sliderBoxNeedsLayout = child.debugNeedsLayout;}); expect(sliderBoxNeedsLayout, isTrue); }); testWidgets('Slider relayout upon system fonts changes', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( home: Material( child: Slider( value: 0.0, onChanged: (double value) { }, ), ), ), ); const Map<String, dynamic> data = <String, dynamic>{ 'type': 'fontsChange', }; await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage( 'flutter/system', SystemChannels.system.codec.encodeMessage(data), (ByteData data) { }, ); // _RenderSlider is the last render object in the tree. final RenderObject renderObject = tester.allRenderObjects.last; expect(renderObject.debugNeedsLayout, isTrue); }); testWidgets('TimePicker relayout upon system fonts changes', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( home: Material( child: Center( child: Builder( builder: (BuildContext context) { return RaisedButton( child: const Text('X'), onPressed: () { showTimePicker( context: context, initialTime: const TimeOfDay(hour: 7, minute: 0), builder: (BuildContext context, Widget child) { return Directionality( key: const Key('parent'), textDirection: TextDirection.ltr, child: child, ); }, ); }, ); }, ), ), ), ), ); await tester.tap(find.text('X')); await tester.pumpAndSettle(); const Map<String, dynamic> data = <String, dynamic>{ 'type': 'fontsChange', }; await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage( 'flutter/system', SystemChannels.system.codec.encodeMessage(data), (ByteData data) { }, ); final RenderObject renderObject = tester.renderObject( find.descendant( of: find.byKey(const Key('parent')), matching: find.byType(CustomPaint), ).first, ); expect(renderObject.debugNeedsPaint, isTrue); }); }