how specify Window for tests? - flutter

I need the screen in the test to look the same as on the physical device(or simulator). How can I do it? In my case device id Iphone SE.
I wrote a test that saves a screenshot to disk:
testWidgets('test', (WidgetTester tester) async {
final AutomatedTestWidgetsFlutterBinding binding = tester.binding;
binding.renderView.configuration = TestViewConfiguration(size: Size(640, 1136));
var widget = Scaffold(
appBar: AppBar(title: Text('title'),),
body: Column(children: <Widget>[
RaisedButton(
child: Text('button'),
onPressed: () {},)
],),
);
var key = new GlobalKey();
await tester.pumpWidget(
MaterialApp(home: RepaintBoundary(key: key, child: widget),),
);
await tester.pumpAndSettle();
await tester.runAsync(() async {
RenderRepaintBoundary boundary = key.currentContext.findRenderObject();
var image = await boundary.toImage();
var byteData = await image.toByteData(format: ImageByteFormat.png);
var pngBytes = byteData.buffer.asUint8List();
await File('screen.png').writeAsBytes(pngBytes);
});
});
if use ViewConfiguration with devicePixelRatio instead TestViewConfiguration, devicePixelRatio ignoring
MediaQuery too don work, if wrap MaterialApp
appbar and button less then on simulator
screen from test:
but expected(widgets scale):

You get the blocks instead of text because Flutter uses a specific test font (Ahem) that has all characters just blocks.
This makes it easier to render them equally on Linux (CI) and other platforms. I don't know if there are other reasons.
I also wasn't able to make images work in golden tests.
https://github.com/flutter/engine/pull/6913 was a recently merged fix to allow loading custom fonts in tests.
You can use flutter run --use-test-fonts to make Flutter use the Ahem font when you run the app on a real device so you can visualize how the test will look.
Related issues
https://github.com/flutter/flutter/issues/24405
https://github.com/flutter/flutter/issues/17910#issuecomment-445184463
I don't know if font's loaded this way work in golden tests though (they might still not work similar to images)
If you want to specify different screen sizes see (not tested myself) How to test Flutter widgets on different screen sizes?
Not sure if this suggestion is still of any value. I found it quite limited and the above suggestion probably works better) In Flutter Widget testing, how to make media.orientation to portrait?

Related

Flutter pick image dynamically for web and mobile

I'm working on an editor which needs to work on both web and mobile. So the core functionalities will remain same but the UI part will change.
Within that core part I have a portion which works with images. As the File object comes from both dart:io and dart:html I am facing some issues with getter setters.
Where I want to show the image:
Widget buildImage() {
return Image.file(
widget.item!.imageFile,
fit: widget.item!.imageFit,
color: widget.item!.color,
colorBlendMode: widget.item!.blendMode,
alignment: widget.item!.alignment,
gaplessPlayback: true,
excludeFromSemantics: true,
);
}
widget.item!.imageFile is a getter setter that I worked on the mobile counter part:
io.File get imageFile => io.File(_image!.image.filename);
set imageFile(io.File value) => _image!.image.filename = value.path;
But as now I will have to make the code work on the web version as well I tried:
if (isWeb) {
await _pickImageWeb();
} else {
// some code for mobile
}
And the _pickImageWeb() is as below:
_pickImageWeb() async {
final ImagePicker _picker = ImagePicker();
XFile? image = await _picker.pickImage(source: ImageSource.gallery);
if (image != null) {
var imageBytes = await image.readAsBytes();
setState(() {});
}
}
At this point I'm completely lost and I think I went the wrong way. Since till now I was my images from the path using getter setter and now in web the image picking is completely different.
Should I have multiple getter setters for both mobile and web i.e: imageFileMobile and imageFileWeb?
Or how to solve the issue in general. I have seen file_picker but couldn't figure out how to integrate the package in this case.
dart:io doesn't work with web, and vice versa for dart:html, I would suggest using cross_file and store the result in XFile and use it as file.readAsBytes(), because flutter web gives fake paths when selecting files, so going with bytes seems to be the best option IMO.

How do I take a screenshot for a PlatformView Widget in Flutter Mobile Application?

Here is the code I use to do a screenshot.
First, I placed the widget inside RepaintBoundary.
RepaintBoundary(
key: widgetKey,
child: childWidget,
),
Then somewhere else in the code, we initiate a screenshot with the following code.
/// Note: Delay added to the actual code ensuring widget finish rendering.
final RenderRepaintBoundary boundary =
widgetKey.currentContext!.findRenderObject() as RenderRepaintBoundary;
final image = await boundary.toImage(pixelRatio: pixelRatio);
final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
The above code works for any widget except PlatformView widgets, which return an empty picture.
I've read the issues on Github #25306 and #83856.
They are closed, but the problem persists.
I've added a 2 seconds delay, but it still doesn't work.
Can someone help? Thanks

How to test widgets integrity with increased font sizes from accessibility options?

In my app, I've noticed some widgets generate overflow exception in a particular situation: when there are increased font sizes preempted by the user via Accessibility options in the iOS/Android.
I would like to widget test these widgets, by passing a parameter to increase the font size like OS selected, and check if there are any overflows.
This kind of test would be similar to widget test in different screen sizes, in which is possible to set the physical size like below:
import 'package:flutter_test/flutter_test.dart';
(...)
testWidgets('test overflow on iPhone SE 1 gen', (tester) async {
tester.binding.window.physicalSizeTestValue = Size(320.0, 568.0);
(...)
}
Are there any recommended practices over widget testing with highly increased font sizes as an accessibility option in Flutter?
You can provide a new MediaQuery when you pump your widget:
await tester.pumpWidget(MaterialApp(
home: Material(
child: Builder(
builder: (context) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.5),
child: YourWidgetHere(),
);
}
),
),
));
Update:
It may be more appropriate to update the test scale factor, although I haven't tested it:
tester.binding.window.platformDispatcher.textScaleFactorTestValue = 2.5;

Text blanked out in Flutter flutter_test widget test golden screenshot

When I try widget testing my flutter app, I am unable to locate the desired widgets by their ID. As a result, I took a golden screenshot to see what was happening and it appears every instance of Text is being blanked out (in the same color as the text color should be).
See side-by-side of expected app appearance and then golden screenshot.
I've simplified my problem down to a finer grained example, see the code here:
testGoldens('Example', (WidgetTester tester) async {
tester.binding.window.physicalSizeTestValue = Size(1284, 2778);
addTearDown(tester.binding.window.clearPhysicalSizeTestValue);
Widget exampleWidget = Scaffold(
appBar: AppBar(),
body: Text('example', style: TextStyle(color: Colors.red)));
await tester.pumpWidget(MaterialApp(home: exampleWidget));
await expectLater(
find.byType(MaterialApp), matchesGoldenFile('signIn.png'));
});
And this produces this screenshot. (Notice even the debug sash is blanked out).
Anyone seen this before?
Thanks 🙏

How to set default size of macos app in flutter?

I am trying to build a macOS desktop app with flutter. I want the app to be full-width, edge-to-edge. However, when I run the app via the simulator, or after the build, it always launches the app with size 800x600.
I have set the height and width of the root container to double.infinity. In fact, even if I set the height and width to 10.0, it always launches the app with 800x600. I am new to flutter, so probably missing some fundamentals. Most tutorials I have come across talk about building a mobile app where this is never a problem because the app always launches to its full width.
Here is my entire test app code:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(color: Colors.white),
height: double.infinity,
width: double.infinity,
child: Center(
child: Text(
'Hello World',
textDirection: TextDirection.ltr,
style: TextStyle(
fontSize: 32, fontWeight: FontWeight.bold, color: Colors.black),
),
),
);
}
}
There's now a plugin to do this, which is not a permanent thing as it is described as preliminary functionality before eventually being folded into the core libraries.
Using the plugin for now is still likely to be better than hard-coding directly modifying the native code, especially if you have multiple platforms you want to work on.
First add to the pubspec.yaml something like:
dependencies:
...
window_size:
git:
url: git://github.com/google/flutter-desktop-embedding.git
path: plugins/window_size
ref: 927f8cbc09b35d85245c095f2db8df9b186f6618
Using the specific Git reference to include this, as shown above, will give you good control over when you choose to pull updated code and make any changes this might entail.
You can then access various functions to set min/max window sizes, or frame, or get the current values, e.g.:
...
import 'dart:io'
import 'package:window_size/window_size.dart';
...
void main() {
WidgetsFlutterBinding.ensureInitialized();
if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
setWindowTitle("My Desktop App");
setWindowMinSize(Size(375, 750));
setWindowMaxSize(Size(600, 1000));
}
runApp(MyApp());
}
I hope this helps someone. I'll try and update this post when the real answer comes out. It seems likely that the interface will approximate what is presented in this library, but the feature set is likely to undergo some change.
Currently the only way to control the initial size is in native code (follow these issues: 1 and 2 to find out when that changes). You'd most likely want to set it in macos/Runner/MainFlutterWindow.swift.
It's not clear from your description whether you want to launch into full-screen mode, or just have a standard window the size of the client area of the screen; the code involved would be different depending on which you are trying to accomplish.
This package can help with it.
Size size = await DesktopWindow.getWindowSize();
print(size);
await DesktopWindow.setWindowSize(Size(500,500));
await DesktopWindow.setMinWindowSize(Size(400,400));
await DesktopWindow.setMaxWindowSize(Size(800,800));
await DesktopWindow.resetMaxWindowSize();
await DesktopWindow.toggleFullScreen();
bool isFullScreen = await DesktopWindow.getFullScreen();
await DesktopWindow.setFullScreen(true);
await DesktopWindow.setFullScreen(false);
I am not sure if this 100% valid, but I was looking for possibility to set window size. I have found package as #karora mentioned, but I wanted to only set window size and move on. So we can make it using xcode.
In project folder open Runner.xcodeproj:
macos -> Runner.xcodeproj
Then in Xcode project find MainMenu.xib then you can resize your flutter window.
You should be able to achieve this now within Dart code by using the window_size plugin.
The code to set initial window size could be something like:
#override
void initState() {
super.initState();
setWindowFrame(Rect.fromLTRB(1200.0, 500.0, 1800.0, 1125.0));
}
somewhere like your top-level stateful widget.
Though I should note that at the moment for me on Linux, the window frame sizing works, but positioning doesn't.
i need set default size for windows desktop app & this solution worked for me and cover the ios and linux platforms.
import 'package:desktop_window/desktop_window.dart'as window_size;
import 'dart:io';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
window_size.DesktopWindow.setMinWindowSize(Size(375, 750));
window_size.DesktopWindow.setMaxWindowSize(Size(600, 1000));
}
runApp(MyApp());
}