Flutter Camera Plugin taking dark image bug - flutter

I am getting dark images from flutter Camera Plugin.
Camera Preview is showing correctly but after taking the picture it becomes too dark.
I searched and what i found that it's about the FPS and exposure of the camera.
How can I solve this problem?
I need to show camera preview and take pictures in my app.
Please don't tell me to use image_picker package.
Device : Redmi note 4
Android OS : 7.0
Here is the Image
dark image
Here is the code
import 'dart:async';
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:path/path.dart' show join;
import 'package:path_provider/path_provider.dart';
Future<void> main() async {
// Obtain a list of the available cameras on the device.
final cameras = await availableCameras();
// Get a specific camera from the list of available cameras.
final firstCamera = cameras.first;
runApp(
MaterialApp(
theme: ThemeData.dark(),
home: TakePictureScreen(
// Pass the appropriate camera to the TakePictureScreen widget.
camera: firstCamera,
),
),
);
}
// A screen that allows users to take a picture using a given camera.
class TakePictureScreen extends StatefulWidget {
final CameraDescription camera;
const TakePictureScreen({
Key key,
#required this.camera,
}) : super(key: key);
#override
TakePictureScreenState createState() => TakePictureScreenState();
}
class TakePictureScreenState extends State<TakePictureScreen> {
CameraController _controller;
Future<void> _initializeControllerFuture;
#override
void initState() {
super.initState();
// To display the current output from the Camera,
// create a CameraController.
_controller = CameraController(
// Get a specific camera from the list of available cameras.
widget.camera,
// Define the resolution to use.
ResolutionPreset.medium,
);
// Next, initialize the controller. This returns a Future.
_initializeControllerFuture = _controller.initialize();
}
#override
void dispose() {
// Dispose of the controller when the widget is disposed.
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Take a picture')),
// Wait until the controller is initialized before displaying the
// camera preview. Use a FutureBuilder to display a loading spinner
// until the controller has finished initializing.
body: FutureBuilder<void>(
future: _initializeControllerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// If the Future is complete, display the preview.
return CameraPreview(_controller);
} else {
// Otherwise, display a loading indicator.
return Center(child: CircularProgressIndicator());
}
},
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.camera_alt),
// Provide an onPressed callback.
onPressed: () async {
// Take the Picture in a try / catch block. If anything goes wrong,
// catch the error.
try {
// Ensure that the camera is initialized.
await _initializeControllerFuture;
// Construct the path where the image should be saved using the
// pattern package.
final path = join(
// Store the picture in the temp directory.
// Find the temp directory using the `path_provider` plugin.
(await getTemporaryDirectory()).path,
'${DateTime.now()}.png',
);
// Attempt to take a picture and log where it's been saved.
await _controller.takePicture(path);
// If the picture was taken, display it on a new screen.
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DisplayPictureScreen(imagePath: path),
),
);
} catch (e) {
// If an error occurs, log the error to the console.
print(e);
}
},
),
);
}
}
// A widget that displays the picture taken by the user.
class DisplayPictureScreen extends StatelessWidget {
final String imagePath;
const DisplayPictureScreen({Key key, this.imagePath}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Display the Picture')),
// The image is stored as a file on the device. Use the `Image.file`
// constructor with the given path to display the image.
body: Image.file(File(imagePath)),
);
}
}

Just put delay before take picture.
Future.delayed(const Duration(milliseconds: 500), () {
_controller.takePicture(path);
});

I think it's not about a delay, images are dark if exposure is not handled.
Also exposure requires focus pre captures to work and are not handled in official plugin now.
You can use this plugin : CamerAwesome
Official plugin has been quite abandonned. This plugin includes flash, zoom, auto focus, exposure... and no initialisation required.
It uses value notifier to change data directly in preview like this :
// init Notifiers
ValueNotifier<CameraFlashes> _switchFlash = ValueNotifier(CameraFlashes.NONE);
ValueNotifier<Sensors> _sensor = ValueNotifier(Sensors.BACK);
ValueNotifier<Size> _photoSize = ValueNotifier(null);
#override
Widget build(BuildContext context) {
return CameraAwesome(
onPermissionsResult: (bool result) { }
selectDefaultSize: (List<Size> availableSizes) => Size(1920, 1080),
onCameraStarted: () { },
onOrientationChanged: (CameraOrientations newOrientation) { },
zoom: 0.64,
sensor: _sensor,
photoSize: _photoSize,
switchFlashMode: _switchFlash,
orientation: DeviceOrientation.portraitUp,
fitted: true,
);
};

A hack that works for me, with camera plugin: take the picture twice. The first one buys time for the second one to have the proper exposure and focus.
final image = await controller.takePicture(); // is not used
final image2 = await controller.takePicture();

Related

How to set ThemeMode in splash screen using value stored in sqflite FLUTTER

I have a Flutter Application where an sqflite database stored the user preference of ThemeMode (viz Dark, Light and System). I have created a splash screen using flutter_native_splash which supports dark mode too.
The Problem is this that I want the splash screen to follow the users stored value for theme mode. Currently, the code I am using is as follows:
class MyRoot extends StatefulWidget {
// const MyRoot({Key? key}) : super(key: key);
static ValueNotifier<ThemeMode> themeNotifier = ValueNotifier(ThemeMode.system);
#override
State<MyRoot> createState() => _MyRootState();
}
class _MyRootState extends State<MyRoot> {
DatabaseHelper? databaseHelper = DatabaseHelper.dhInstance;
ThemeMode? tmSaved;
#override
void initState() {
Future.delayed(Duration.zero, () async => await loadData());
super.initState();
}
#override
Widget build(BuildContext context) {
//to prevent auto rotation of the app
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
return ValueListenableBuilder<ThemeMode>(
valueListenable: MyRoot.themeNotifier,
builder: (_, ThemeMode currentMode, __) {
return Sizer(
builder: (context, orientation, deviceType) {
return MaterialApp(
title: 'My Application',
theme: themeLight, //dart file for theme
darkTheme: themeDark, //dart file for theme
themeMode: tmSaved ?? currentMode,
initialRoute: // my initial root
routes: {
// my routes
.
.
.
// my routes
},
);
},
);
},
);
}
Future<void> loadData() async {
if (databaseHelper != null) {
ThemeMode? themeMode= await databaseHelper?.selectStoredTheme(); // function retrieving sqflite stored value and returning ThemeMode value
if (themeMode != null) {
MyRoot.themeNotifier.value = themeMode;
return;
}
}
MyRoot.themeNotifier.value = ThemeMode.system;
}
}
Currently, this shows a light theme splash screen loading, then converts it into dark with a visible flicker.
ValueListenableBuilder<ThemeMode>(... is to enable real time theme change from settings page in my app which working as intended (taken from A Goodman's article: "Flutter: 2 Ways to Make a Dark/Light Mode Toggle".
main.dart has the below code:
void main() {
runApp(MyRoot());
}
Have you tried loading the setting from sqflite in main() before runApp? If you can manage to do so, you should be able to pass the setting as argument to MyRoot and then the widgets would be loaded from the start with the correct theme. I'm speaking in theory, I can't test what I'm suggesting right now.
Something like:
void main() async {
ThemeMode? themeMode= await databaseHelper?.selectStoredTheme(); // function retrieving sqflite stored value and returning ThemeMode value
runApp(MyRoot(themeMode));
}
[...]
class MyRoot extends StatefulWidget {
ThemeMode? themeMode;
const MyRoot(this.themeMode, {Key? key}) : super(key: key);
static ValueNotifier<ThemeMode> themeNotifier = ValueNotifier(ThemeMode.system);
#override
State<MyRoot> createState() => _MyRootState();
}
EDIT
Regarding the nullable value you mentioned in comments, you can change the main like this:
void main() async {
ThemeMode? themeMode= await databaseHelper?.selectStoredTheme(); // function retrieving sqflite stored value and returning ThemeMode value
themeMode ??= ThemeMode.system;
runApp(MyRoot(themeMode!));
}
which makes themeMode non-nullable, and so you can change MyRoot in this way:
class MyRoot extends StatefulWidget {
ThemeMode themeMode;
const MyRoot(required this.themeMode, {Key? key}) : super(key: key);
[...]
}
Regarding the functionality of ValueNotifier, I simply thought of widget.themeMode as the initial value of your tmSaved property in your state, not as a value to be reused in the state logic. Something like this:
class _MyRootState extends State<MyRoot> {
DatabaseHelper? databaseHelper = DatabaseHelper.dhInstance;
late ThemeMode tmSaved;
#override
void initState() {
tmSaved = widget.themeMode;
super.initState();
}
[...]
}
so that your widgets would already have the saved value at the first build.
PS the code in this edit, as well as in the original part, isn't meant to be working by simply pasting it. Some things might need adjustments, like adding final to themeMode in MyRoot.
Make your splashscreen. A main widget which get data from sqlflite
And make splashscreen widget go to the your home widget with remove it using navigation pop-up
for example :
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'ToDo',
color: // color of background
theme: // theme light ,
darkTheme: // darktheme
themeMode: // choose default theme light - dark - system
home: Splashscreen(),// here create an your own widget of splash screen contains futurebuilder to fecth data and return the mainWidget ( home screen for example)
);
}
}
class Splashscreen extends StatelessWidget {
Future<bool> getData()async{
// get info
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getData(),
builder: (context,snapshot){
// if you want test snapshot
//like this
if(snapshot.hasData) {
return Home();
} else {
return Container(color: /* background color as same as theme's color */);
}
}
);
}
}

Result of consuming a Future with future.then() seems to only live inside the .then function()

I am building an app as a project for university and one of the requirements is to play videos within the app.
I have links to exercise videos (bicep curls and so on) stored in a column of a table in SQLite.
I am using Moor in order to interact with the database.
I have the following screen where I am trying to have the video referred in the link from the database play:
class ExerciseVideoTab extends StatefulWidget {
final int exerciseId;
ExerciseVideoTab(this.exerciseId);
#override
_ExerciseVideoTabState createState() => _ExerciseVideoTabState();
}
class _ExerciseVideoTabState extends State<ExerciseVideoTab> {
VideoPlayerController _controller;
Future<void> _initializeVideoPlayerFuture;
String _exerciseVideoLink;
#override
void initState() {
super.initState();
locator<MoorDB>().getExerciseById(widget.exerciseId).then((value) =>
_exerciseVideoLink = value.exerciseVideoLink);
_controller = VideoPlayerController.network(_exerciseVideoLink.toString());
_initializeVideoPlayerFuture = _controller.initialize();
print(_exerciseVideoLink); // prints null for some reason
}
#override
void dispose() {
// Ensure disposing of the VideoPlayerController to free up resources.
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: FutureBuilder(
future: _initializeVideoPlayerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// If the VideoPlayerController has finished initialization, use
// the data it provides to limit the aspect ratio of the video.
return AspectRatio(
aspectRatio: _controller.value.aspectRatio,
// Use the VideoPlayer widget to display the video.
child: VideoPlayer(_controller),
);
} else {
// If the VideoPlayerController is still initializing, show a
// loading spinner.
return Center(child: CircularProgressIndicator());
}
}
)
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// Wrap the play or pause in a call to `setState`. This ensures the
// correct icon is shown.
setState(() {
// If the video is playing, pause it.
if (_controller.value.isPlaying) {
_controller.pause();
} else {
// If the video is paused, play it.
_controller.play();
}
});
},
// Display the correct icon depending on the state of the player.
child: Icon(
_controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
),
),
);
}
}
I am trying to consume the Future<Exercise> that is returned by the getExerciseById(int id) method and assign the exerciseVideoLink column value to the local _exerciseVideoLink and then use that String to initialize the VideoPlayerController with the link contained within.
The implementation of getExerciseById(int id) is the following:
Future<Exercise> getExerciseById(int id) {
return (select(exercises)..where((exercise) => exercise.exerciseId.equals(id))).getSingle();
}
My problem right now is that after consuming the Future<Exercise> and assigning its exerciseVideoLink attribute to the local String variable, the variable becomes null as soon as the .then((value) => ... function is over and thus, the initialization of the VideoPlayerController fails because the URI is null.
Why is that? How can I make it so that I can consume the Future<Exercise> and use its exerciseVideoLink in order to pass it to the VideoPlayerController?
Your _controller depends on the result of getExerciseById() so you need to wait for that Future to complete before you can assign it. You may find async/await syntax a bit easier to read when working with lots of nested Futures.
An example implementation could be:
#override
void initState() {
super.initState();
_init(); // split out a separate method, initState cannot be async
}
Future<void> _init() async {
final exercise = await locator<MoorDB>().getExerciseById(widget.exerciseId);
_controller = VideoPlayerController.network(exercise.exerciseVideoLink.toString());
_initializeVideoPlayerFuture = _controller.initialize();
}

How to wait for camera to initialize?

I kept getting an error from the camera.dart that "name" was being called on a null object.
After some time, I realized that the problem was the that the build method is called before the async code in my initstate finished (I'm actually slightly proud that I understood the problem at least :))
I tried many different ways to initialize my camera properly, but I could not.
This is the last iteration of my code.
What's the idiomatic way of handling this future?
class _PicturePreviewState extends State<PicturePreview> {
List<CameraDescription> cameras;
CameraDescription camera;
CameraController cameraController;
Future<void> initializeController;
Future<void> getCameras() async {
try {
cameras = await availableCameras();
} catch(e) {print(e);}
camera = cameras.last;
print(camera);
}
#override
void initState() {
super.initState();
// getCameras();
availableCameras().then((availableCameras) {
cameras = availableCameras;
camera = cameras.first;
cameraController = CameraController(
camera,
ResolutionPreset.low,
);
initializeController = cameraController.initialize();
print(cameraController.value.isInitialized);
});
// cameraController = CameraController(
// camera,
// ResolutionPreset.low,
// );
// initializeController = cameraController.initialize();
// print(cameraController.value.isInitialized);
}
#override
void dispose() {
cameraController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<void>(
future: initializeController,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// If the Future is complete, display the preview.
return CameraPreview(cameraController);
}
else {
// Otherwise, display a loading indicator.
print(snapshot.connectionState);
return Center(child: CircularProgressIndicator());
}
},
),
I have been relying on this page to use the camera package, but I could not use it verbatim because I can't keep passing down the camera object down my widget tree.
I fixed it.
I put the initializing of the camera object in the in the parent of the widget.
class _TakeReceiptPictureState extends State<TakeReceiptPicture> {
List<CameraDescription> cameras;
CameraDescription camera;
#override
void initState() {
super.initState();
availableCameras().then((availableCameras) {
cameras = availableCameras;
camera = cameras.first;
});
}
Then made the widget that takes the picture have a parameter of type CameraDescription.
class PicturePreview extends StatefulWidget {
final CameraDescription camera;
const PicturePreview(this.camera, {Key key}) : super(key: key);
#override
_PicturePreviewState createState() => _PicturePreviewState();
}
Then passed the camera initialized in the parent to picture widget
onTap: () {
Navigator.of(context).push(
PageTransition(
type: PageTransitionType.transferRight,
child: PicturePreview(camera)),
);
}),
by the time the child widget's build method runs, the camera object is already initialized and ready to go.
Now the state of the child have only two variables, the camera controller and the initialize controller future.
CameraController cameraController;
Future<void> initializeController;
#override
void initState() {
super.initState();
cameraController = CameraController(
widget.camera,
ResolutionPreset.low,
);
initializeController = cameraController.initialize();
}
TLDR: let the initialization of the camera object be the responsibility of the parent of the widget.

flutter for web: Get width/height form NetworkImage before displaying it

I want to get attribute values, such as width and height, from NetworkImage or Image.network before displaying the image.
I found the following good posts, but it doesn't work. It got the size values, but the image is not loaded in FutureBuilder.
How do I determine the width and height of an image in Flutter?
How do I tell when a NetworkImage has finished loading?
My code is as below;
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: FutureBuilder(
future: _getImage(),
builder: (BuildContext context, AsyncSnapshot<Image> snapshot) {
print(snapshot.hasData);
if (snapshot.hasData) {
return snapshot.data;
} else {
return Text('Loading...');
}
},
),
),
);
}
Future<Image> _getImage() async {
final Completer completer = Completer();
final String url = 'http://images-jp.amazon.com/images/P/4101098018.09.MZZZZZZZ';
final image = NetworkImage(url);
image.resolve(ImageConfiguration())
.addListener(ImageStreamListener((ImageInfo info, bool isSync) {
print(info.image.width);
completer.complete(info.image);
}));
return completer.future;
}
}
The result is;
- The screen only shows "Loading..." and the image is not loaded.
- print output is as below. This should means, FutureBuilder is called twice before loading the image, and we can get the width but FutureBuilder is not called after that.
false
false
112
Environment:
Flutter 1.13.0 • channel dev (due to flutter web)
Chrome Version 79.0.3945.79
Couple of observations based on the reference post you provided.
You have mixed up ui.Image with Image widget.
If you separate the info logic from widget building then you will not have access to the Image widget meaning you will have to recreate it. Instead you can create a widget and return it.
In your http based answer response.body.length might not exactly represent the image dimension. You have to see if the response headers has any information about the image.
Also note FutureBuilder's build method will be called more than once with different ConnectionState depending on the state of the future like waiting or done. Check here
Option 1:
If you don't care about the Image widget then your current code can work with some slight modification. This is exactly identical to the original post but modified to match to the way you defined it in your post.
import 'dart:async';
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
class ImageSizeTestWidget extends StatefulWidget {
ImageSizeTestWidget({Key key, this.title}) : super(key: key);
final String title;
#override
_ImageSizeTestWidgetState createState() => _ImageSizeTestWidgetState();
}
class _ImageSizeTestWidgetState extends State<ImageSizeTestWidget> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: FutureBuilder<ui.Image>(
future: _getImage(),
builder: (BuildContext context, AsyncSnapshot<ui.Image> snapshot) {
print(snapshot.hasData);
if (snapshot.hasData) {
return Text('${snapshot.data.width} X ${snapshot.data.height}');
} else {
return Text('Loading...');
}
},
),
),
);
}
Future<ui.Image> _getImage() async {
final Completer<ui.Image> completer = Completer<ui.Image>();
final String url =
'http://images-jp.amazon.com/images/P/4101098018.09.MZZZZZZZ';
Image image = Image.network(url);
image.image
.resolve(ImageConfiguration())
.addListener(ImageStreamListener((ImageInfo info, bool isSync) {
print(info.image.width);
completer.complete(info.image);
}));
return completer.future;
}
}
Option 2:
Just use the code as is in the original post bringing the Image widget creation and information extraction into the build method.
Based on the advice from Abion47, I successfully get the image with http package. But I still cannot get width and/ or height values even after getting the image.
Alternatively, I use response.body.length to check whether the downloaded image is valid or not.
Future<Image> _getImage() async {
Image _image;
final String url = 'http://images-jp.amazon.com/images/P/4101098018.09.MZZZZZZZ';
var response = await http.get(url);
print("Response status: ${response.statusCode}"); // 200
_image = Image.memory(response.bodyBytes);
print(_image.width); // still null
print(response.body.length); // this shows numbers. I'll use this.
return _image;
}
You are halfway there with your self-answer code. From there, you can convert the bytes to a ui.Image object with instantiateImageCodec.
Future<Image> _getImage() async {
Image _image;
final String url = 'http://images-jp.amazon.com/images/P/4101098018.09.MZZZZZZZ';
var response = await http.get(url);
print("Response status: ${response.statusCode}"); // 200, ideally
final bytes = response.bodyBytes);
final codec = await instantiateImageCodec(bytes);
final frame = await codec.getNextFrame();
final uiImage = frame.image; // a ui.Image object, not to be confused with the Image widget
print(uiImage.width); // The width of the image in pixels
print(uiImage.height); // The heightof the image in pixels
_image = Image.memory(bytes);
return _image;
}
It kind of sucks that you have to do it this way as the image will get decoded twice, but short of creating your own custom Image widget that can take the ui.Image object directly, I don't think there's much that can be done about that.

How to mock navigation arguments for testing flutter screen widgets?

I would like to write a mockito test for a screen widget in flutter. The problem is, that this widget uses a variable from the navigation argument and I'm not sure how to mock this variable.
This is the example screen:
class TestScreen extends StatefulWidget {
static final routeName = Strings.contact;
#override
_TestScreenState createState() => _TestScreenState();
}
class _TestScreenState extends State<TestScreen> {
Contact _contact;
#override
Widget build(BuildContext context) {
_contact = ModalRoute.of(context).settings.arguments;
return Scaffold(
appBar: AppBar(title: Text(Strings.contact)),
body: Text(_contact.name),
);
}
}
With this command I open the screen
Navigator.pushNamed(context, TestScreen.routeName, arguments: contact);
Normally I would mock some components, but I'm not sure how to mock the screen arguments. I hope it works something like this. However, I do not know what I can exactly mock.
when(screenArgument.fetchData(any))
.thenAnswer((_) async => expectedContact);
This is the current test, which of course is not working since _contact is null:
void main() {
testWidgets('contact fields should be filled with data from argument', (WidgetTester tester) async {
// GIVEN
final testScreen = TestApp(widget: TestScreen());
// WHEN
await tester.pumpWidget(testScreen);
// THEN
expect(find.text("test"), findsOneWidget);
});
}
An ugly way would be to use constructor parameters for the screen only for testing, but I want to avoid that.
Maybe someone of you knows how to best test such screen widgets.
The way that I've found is the same approach how flutter guys are testing it:
https://github.com/flutter/flutter/blob/d03aecab58f5f8b57a8cae4cf2fecba931f60673/packages/flutter/test/widgets/navigator_test.dart#L715
Basically they create a MaterialApp, put a button that after pressing will navigate to the tested page.
My modified solution:
Future<void> pumpArgumentWidget(
WidgetTester tester, {
#required Object args,
#required Widget child,
}) async {
final key = GlobalKey<NavigatorState>();
await tester.pumpWidget(
MaterialApp(
navigatorKey: key,
home: FlatButton(
onPressed: () => key.currentState.push(
MaterialPageRoute<void>(
settings: RouteSettings(arguments: args),
builder: (_) => child,
),
),
child: const SizedBox(),
),
),
);
await tester.tap(find.byType(FlatButton));
await tester.pumpAndSettle(); // Might need to be removed when testing infinite animations
}
This approach works ok-ish, had some issues with testing progress indicators as it was not able to find those even when debugDumpApp displayed them.
If you are using a Dependency Injector such as I am, you may need to avoid pass contextual arguments to the constructor if your view is not built at the time the view class is instantiated. Otherwise, just use the view constructor as someone suggested.
So if you can't use constructor as I can't, you can solve this using Navigator directly in your tests. Navigator is a widget, so just use it to return your screen. Btw, it has no problem with Progress Indicator as pointed above.
import 'package:commons/core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
class MyCustomArgumentsMock extends Mock implements MyCustomArguments {}
void main() {
testWidgets('indicator is shown when screen is opened', (tester) async {
final MyCustomArguments mock = MyCustomArgumentsMock();
await tester.pumpWidget(MaterialApp(
home: Navigator(
onGenerateRoute: (_) {
return MaterialPageRoute<Widget>(
builder: (_) => TestScreen(),
settings: RouteSettings(arguments: mock),
);
},
),
));
expect(find.byType(CircularProgressIndicator), findsOneWidget);
});
}