InAppWebView getContentHeight always returns 0 on Android - flutter

I use InAppWebView to display a web content in our app.
To ensure that it only takes the necessary space I listen to the onLoadStop callback to then set a SizedBoxs height.
There I try to get the content height:
final height = await controller.getContentHeight();
But it always returns 0.
On iOS I always get the right height.

The problem is that Android calls the onPageFinished callback for the Android Webview too early.
Adding
if (Platform.isAndroid) {
await Future.delayed(
const Duration(milliseconds: 1000),
);
}
solves the problem and
final height = await controller.getContentHeight();
returns the correct height.

Related

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 add back button to WebView in Flutter to dismiss the WebView altogether?

Currently when I tap on an image, it should open a web view as the image object has an associated url. I am using the url_launcher library for Flutter, and I have implemented the code as follows:
onTap: () async {
final url = image.url;
if (await canLaunch(url)) {
await launch(
url,
forceSafariVC: true,
forceWebView: true,
enableJavaScript: true,
);
}
},
My understanding is that this launches a WebView, which is an in-app browser rather than taking the user out of the app and into a separate browser app. This works fine, but the page loads much slower and Javascript elements do not work properly (e.g. animated text on websites). On the other hand,
onTap: () async {
final url = banners[index].url;
if (await canLaunch(url)) {
await launch(
url
);
}
},
works much better as it is faster and everything loads in properly, but it opens an external browser, which is not what I want.
Another issue is that I would like to add a Done button on the top left corner of the WebView to completely exit the WebView and return to where I was in the app. I only have a back button on the bottom left (on the Android emulator), that lets me go to the previous page I was at in the browser.
How do I customise the layout of the WebView, if I do not have any access to it? The url_launcher seems to handle the WebView creation internally, so I'm wondering how can I gain access from the above code to add that button?
Thank you!
If you use the native webview in this manner then you can't customise the ui. But instead, since you are displaying image you can use image.network from flutter.
Image.network(imgURL,fit: BoxFit.fill,
loadingBuilder:(BuildContext context, Widget child,ImageChunkEvent loadingProgress) {
if (loadingProgress == null)
return child;
return Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null ?
loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes
: null,
),
);
},
),
Or, you can also use official webview plugin from flutter team to create in app web view.
webview_flutter: ^2.3.1
In this approaches if you add a back button in ui you can the use Navigator.pop(context); on onPressed property in button to go back

RenderBox size returns 0 even after WidgetsBinding addPostFrameCallback

So I'm developing an application where the user picks an image (either from camera or gallery) and I need to get the size of the widget where this image is loaded to draw some lines on top of it.
The problem is that I'm trying to get RenderBox.size of this widget on WidgetsBinding.instance.addPostFrameCallback and it has 0 width and height.
Some code:
WidgetsBinding.instance.addPostFrameCallback((_) {
final RenderBox renderBox = _keyPic.currentContext.findRenderObject();
final size = renderBox.size;
});
Image is loaded using the code below:
Image.memory(_file.readAsBytesSync(), key: _keyPic)
I'm really not sure what's going on, if someone can shed a light on this issue would be truly appreciated!
I had the same problem with finding the size of images. I used async method instead of adding a one time post frame callback. As async functions will run after all current processes get finished, unless when there is await keyword.
Widget build(BuildContext context) {
Future(yourFunction);
...
}
Future<void> yourFunction() async {
await Future.delayed(Duration.zero); // add a dummy waiting time
...
// thses codes will run after build,
}

tester.scrollUntilVisible() not working with GridView in flutter integration testing

I am trying to scroll and find widget from the GridView in integration testing in flutter. But the code not working:
tester.scrollUntilVisible(itemFinder, -100, scrollable: gridViewFinder)
But this is not working. It is saying GridView is not scrollable.
There are few steps I made mistake here:
scrollUntilVisible() is Future, so
await tester.scrollUntilVisible(...);
to scroll down, delta should be positive:
await tester.scrollUntilVisible(itemFinder, 100.0, ...);
if there is only one list in screen, I don't have to include scrollable:
await tester.scrollUntilVisible(itemFinder, 100.0);
Now done!
Final answer:
await tester.scrollUntilVisible(itemFinder, 100.0);

how specify Window for tests?

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?