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

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);

Related

Flutter reverse listview refresh from bottom (after version >= 3)

After Flutter 3.0 the ListView(reverse: true) in my project has changed behaviour. In older version it can be refreshed from bottom pull but now it doesn't, it can just refresh from top.
I know pull_to_refresh package but I am looking for a core solution like old days.. Github issues indicate that It is intended but I don't wanna reverse flutter version.
Does anybody have idea on flutter 3 and without extra packages?
RefreshIndicator(
key: refreshKey,
onRefresh: () async { onRefresh(); },
child: buildMessagesView(messages),
),
ListView buildMessagesView(List<MessageModel> messages) {
return ListView.builder(
scrollDirection: Axis.vertical,
reverse: true,
itemCount: messages.length,
itemBuilder: (_, index) {
return Message(message: messages.elementAt(index));
},
);
}
while ListView reverse:true I expected the Refresh Indicator work by pull from down but it doesn't..
UPDATE
Solved. The ScrollController answer was very useful for me and I developed a simple widget that wraps the ListView to integrate it into the project. You can find the code here.
https://github.com/bnurd/reversed_listview_refresh
I saw the github issue. Actually I realized this behaviour without refresh indicator widget. You can use ScrollController. Create ScrollController instance with listener, attach it to ListView. Write code inside listener that check whether scroll ended. This is listener function sample code:
void _onScroll() {
final maxScroll = _scrollController.position.maxScrollExtent;
final currentScroll = _scrollController.position.pixels;
if (maxScroll - currentScroll == 0) {'YOUR REFRESH CODE'}
}
This is how you attach this listener to ScrollController:
late final _scrollController = ScrollController()..addListener(_onScroll);
IMPORTANT: Dont' forget to attach scrollController to ListView.

How to sync the Stack building order *Not the Z-order

Description:
I have a Stack with two Widgets. Widget1 (as a background) being a Grid of 4 AssetImages and Widget2 being a semi-transparent rectangular coloured Container building on top of Widget1. The Stack is building both widgets as expected!
Problem:
Widget2 is visually rendered before Widget1, which leads to an awkward effect because Widget2 is rendered over an empty background. I assume that this is caused by Stack async building of children however AssetImage is a synchronous call...
Question:
How to make Widget2 rendering to wait for widget1 until Widget1 gets fully visual as the background?
Notice that I have already tried WidgetBindings.instance.addPostframeCallback
Many Thanks,
Solution 1
I think there is no need to wait for widget1. You can pre-cache the widget1 and widget2 assets with precacheImage way before displaying them. This way they're going to show almost immediately.
await precacheImage(AssetImage('assets/widget1.png'), context);
await precacheImage(AssetImage('assets/widget2.png'), context);
From the docs, after the call of precacheImage:
If the image is later used by an Image or BoxDecoration or FadeInImage, it will probably be loaded faster. The consumer of the image does not need to use the same ImageProvider instance. The ImageCache will find the image as long as both images share the same key, and the image is held by the cache.
Solution 2
Otherwise, you can use Future.delayed to wait a bit for the widget1 to get rendered. The call to delayed should setState of some flag that builds the widget2, let's say bool _buildWidget2 = false flag. Something like the below code snippet:
Future.delayed(
Duration(milliseconds: 150),
() => setState(() => _buildWidget2 = true),
);
And then on the Stack it should be something:
Stack(
children: [
Widget1(),
if (_buildWidget2)
Widget2(),
],
)
If it is me, use a timer to call widget2 late (process animation)
Or I guess I'll just use imageProvider.
https://api.flutter.dev/flutter/painting/ImageProvider-class.html

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

Flutter Bottom Container Swipe Up

I'm making an app which shows place detail when clicking a place on Google Maps. I can open a container at bottom of the page but I want to do that when the user swipe this container to up with animation, the container will cover the whole page and I will get extra information about that place and show the user.
How can I do that with Flutter?
You can try to use the package here:
https://pub.dev/packages/sliding_up_panel
However, if this does not fullfill you wish, try this instead: Wrap you Widget with a GestureDetector and an AnimatedController:
double containerHeight = 0;
GestureDetector(
onVerticalDragEnd: (dragUpdateDetails) {
setState(){
containerHeight = //device height or use MediaQuery.of(context).size.height//
}
},
child: AnimatedContainer(
duration: Duration(milliseconds: //how long should it take//),
height: containerHeight,
child: //whatever you want//
)
),
AnimatedController automatically animates between changes of properties. However, you might want to try changing onVertivalDragEnd to something else like onVerticalDragUpdate to fullfill your wish completely. If you want to have that behaviour for the full screen, wrap your first return Widget with the GestureDetector.
I guess DragableScrollableSheet fits your need. There is a good explanation here in the documentation.
You can use DraggableScrollableSheet
Document and Video tutorial available here:
https://api.flutter.dev/flutter/widgets/DraggableScrollableSheet-class.html

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?