Permission Handler flutter multiple permissions error - flutter

Iam trying to add two permissions i.e. storage and camera. I have done the required setup in androidmanifest.xml file and in the main.dart called both the requests using switch. It gives the error but the app works and the permissions are also updated
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:teamup/Connectivity/network_binding.dart';
import 'package:teamup/Views/Login/sign_in.dart';
import 'package:teamup/Views/OnboardingScreen/onboarding_screen.dart';
import 'Views/OnboardingScreen/onboarding_constants.dart';
void main() async {
await GetStorage.init();
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late Permission cameraPermission;
late Permission storagePermission;
PermissionStatus cameraPermissionStatus = PermissionStatus.denied;
PermissionStatus storagePermissionStatus = PermissionStatus.denied;
void _listenForPermission() async {
final cameraStatus = await Permission.camera.status;
final storageStatus = await Permission.storage.status;
setState(() {
cameraPermissionStatus = cameraStatus;
storagePermissionStatus = storageStatus;
});
switch (cameraStatus) {
case PermissionStatus.denied:
requestForPermission();
break;
case PermissionStatus.granted:
break;
case PermissionStatus.restricted:
Navigator.pop(context);
break;
case PermissionStatus.limited:
Navigator.pop(context);
break;
case PermissionStatus.permanentlyDenied:
Navigator.pop(context);
break;
}
switch (storageStatus) {
case PermissionStatus.denied:
requestForPermission();
break;
case PermissionStatus.granted:
break;
case PermissionStatus.restricted:
Navigator.pop(context);
break;
case PermissionStatus.limited:
Navigator.pop(context);
break;
case PermissionStatus.permanentlyDenied:
Navigator.pop(context);
break;
}
}
Future<void> requestForPermission() async {
final cameraStatus = await Permission.camera.request();
final storageStatus = await Permission.storage.request();
setState(() {
cameraPermissionStatus = cameraStatus;
storagePermissionStatus = storageStatus;
});
}
#override
void initState() {
super.initState();
_listenForPermission();
isViewed.writeIfNull('key', 0);
print(isViewed.read('key'));
}
#override
Widget build(BuildContext context) {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(primarySwatch: Colors.blue, fontFamily: 'Glory'),
initialBinding: NetworkBinding() ,
home: isViewed.read('key') != 0
? const SignInPage()
: const OnboardingPage(),
);
}
}
This is the message that was displayed after installation
Launching lib\main.dart on Redmi Note 4 in debug mode...
Running Gradle task 'assembleDebug'...
√ Built build\app\outputs\flutter-apk\app-debug.apk.
Installing build\app\outputs\flutter-apk\app.apk...
W/FlutterActivityAndFragmentDelegate(21677): A splash screen was provided to Flutter, but this is deprecated. See flutter.dev/go/android-splash-migration for migration steps.
Debug service listening on ws://127.0.0.1:56092/dh3Yn3XCkGg=/ws
Syncing files to device Redmi Note 4...
I/FA (21677): Tag Manager is not found and thus will not be used
I/flutter (21677): 0
[GETX] Instance "GetMaterialController" has been created
[GETX] Instance "GetMaterialController" has been initialized
[GETX] Instance "GetXNetworkManager" has been created
[GETX] Instance "GetXNetworkManager" has been initialized
I/Timeline(21677): Timeline: Activity_launch_request time:267480997 intent:Intent { act=android.content.pm.action.REQUEST_PERMISSIONS pkg=com.google.android.packageinstaller (has extras) }
E/flutter (21677): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: PlatformException(PermissionHandler.PermissionManager, A request for permissions is already running, please wait for it to finish before doing another request (note that you can request multiple permissions at the same time)., null, null)
E/flutter (21677): #0 StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:607:7)
E/flutter (21677): #1 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:156:18)
E/flutter (21677): <asynchronous suspension>
E/flutter (21677): #2 MethodChannelPermissionHandler.requestPermissions (package:permission_handler_platform_interface/src/method_channel/method_channel_permission_handler.dart:71:9)
E/flutter (21677): <asynchronous suspension>
E/flutter (21677): #3 PermissionActions.request (package:permission_handler/permission_handler.dart:44:31)
E/flutter (21677): <asynchronous suspension>
E/flutter (21677): #4 _MyAppState.requestForPermission (package:teamup/main.dart:75:26)
E/flutter (21677): <asynchronous suspension>
E/flutter (21677):
I/Timeline(21677): Timeline: Activity_launch_request time:267501469 intent:Intent { act=android.content.pm.action.REQUEST_PERMISSIONS pkg=com.google.android.packageinstaller (has extras) }

The switch-statements in _listenForPermission will call requestForPermission twice, if both cameraStatus and storageStatus have status PermissionStatus.denied. Therefore you get the exception:
PlatformException(PermissionHandler.PermissionManager, A request for permissions is already running, please wait for it to finish before doing another request (note that you can request multiple permissions at the same time)., null, null)
The permission_handler package allows you to request multiple permission in one call like this:
// You can request multiple permissions at once.
Map<Permission, PermissionStatus> statuses = await [
Permission.location,
Permission.storage,
].request();
print(statuses[Permission.location]);

Related

Flutter "globaloverlay.support()" doesn't work when its on material app

Well the problem I am having here, normally if I wrap materialApp with global overlay support, it should be working, but it is not as you can see from debug console. Any suggestions? I am normally trying integrate firebase on iOS. The restart app is causing problem I guess, but I do not want to throw it out, to saying truth
// ignore_for_file: prefer_const_constructors, duplicate_ignore
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:medicte/pages/home_page.dart';
import 'package:animated_splash_screen/animated_splash_screen.dart';
import 'package:overlay_support/overlay_support.dart';
import 'package:page_transition/page_transition.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';
Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
if (kDebugMode) {
print("Handling a background message: ${message.messageId}");
}
}
main() {
runApp(RestartWidget(child: MyApp()));
}
Future<void> initializeDefault() async {
FirebaseApp app = await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
if (kDebugMode) {
print('Initialized default app $app');
}
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
// ignore: prefer_const_constructors
return ScreenUtilInit(
designSize: const Size(428, 926),
builder: (context, child) => OverlaySupport.global(
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: SplashScreen(),
),
),
);
}
}
class RestartWidget extends StatefulWidget {
const RestartWidget({Key? key, required this.child}) : super(key: key);
final Widget child;
static void restartApp(BuildContext context) {
context.findAncestorStateOfType<_RestartWidgetState>()?.restartApp();
}
#override
// ignore: library_private_types_in_public_api
_RestartWidgetState createState() => _RestartWidgetState();
}
class _RestartWidgetState extends State<RestartWidget> {
late final FirebaseMessaging _messaging;
PushNotification? _notificationInfo;
void requestAndRegisterNotification() async {
// 1. Initialize the Firebase app
await initializeDefault();
// 2. Instantiate Firebase Messaging
_messaging = FirebaseMessaging.instance;
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
// 3. On iOS, this helps to take the user permissions
NotificationSettings settings = await _messaging.requestPermission(
alert: true,
badge: true,
provisional: false,
sound: true,
);
if (settings.authorizationStatus == AuthorizationStatus.authorized) {
if (kDebugMode) {
print('User granted permission');
}
String? token = await _messaging.getToken();
if (kDebugMode) {
print("The token is ${token!}");
}
// For handling the received notifications
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
// Parse the message received
PushNotification notification = PushNotification(
title: message.notification?.title,
body: message.notification?.body,
);
if (kDebugMode) {
print("The notification is $notification");
}
setState(() {
_notificationInfo = notification;
});
if (_notificationInfo != null) {
// For displaying the notification as an overlay
showSimpleNotification(
Text(_notificationInfo!.title!),
subtitle: Text(_notificationInfo!.body!),
background: Colors.cyan.shade700,
duration: Duration(seconds: 2),
);
}
});
} else {
if (kDebugMode) {
print('User declined or has not accepted permission');
}
}
}
#override
void initState() {
requestAndRegisterNotification();
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
PushNotification notification = PushNotification(
title: message.notification?.title,
body: message.notification?.body,
);
setState(() {
_notificationInfo = notification;
});
});
super.initState();
}
Key key = UniqueKey();
void restartApp() {
setState(() {
key = UniqueKey();
});
}
#override
Widget build(BuildContext context) {
return KeyedSubtree(
key: key,
child: widget.child,
);
}
}
class SplashScreen extends StatelessWidget {
const SplashScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return AnimatedSplashScreen(
splash: Image.asset(
'lib/images/MedicteLogo-4a2e31cd2358bb08ff8d12acb2761357.png'),
nextScreen: HomePage(),
splashTransition: SplashTransition.scaleTransition,
pageTransitionType: PageTransitionType.rightToLeftWithFade,
);
}
}
class PushNotification {
PushNotification({
this.title,
this.body,
});
String? title;
String? body;
}
Heading
This is my debug console:
>2022-09-02 12:02:26.023971+0300 Runner[19573:866637] Metal API Validation Enabled
2022-09-02 12:02:26.194783+0300 Runner[19573:866637] Could not load the "LaunchImage" image referenced from a nib in the bundle with identifier "com.example.medicte"
2022-09-02 12:02:26.208267+0300 Runner[19573:866829] 9.4.0 - [FirebaseCore][I-COR000012] Could not locate configuration file: 'GoogleService-Info.plist'.
2022-09-02 12:02:26.236111+0300 Runner[19573:866823] 9.4.0 - [FirebaseCore][I-COR000003] The default Firebase app has not yet been configured. Add `FirebaseApp.configure()` to your application initialization. This can be done in in the App Delegate's application(_:didFinishLaunchingWithOptions:)` (or the `#main` struct's initializer in SwiftUI). Read more: https:/goo.gl/ctyzm8.
2022-09-02 12:02:26.237231+0300 Runner[19573:866823] 9.4.0 - [FirebaseCore][I-COR000003] The default Firebase app has not yet been configured. Add `FirebaseApp.configure()` to your application initialization. This can be done in in the App Delegate's application(_:didFinishLaunchingWithOptions:)` (or the `#main` struct's initializer in SwiftUI). Read more: https:/goo.gl/ctyzm8.
2022-09-02 12:02:26.279437+0300 Runner[19573:866849] flutter: The Dart VM service is listening on http://127.0.0.1:51958/HXJzT6ZmSJY=/
2022-09-02 12:02:26.614633+0300 Runner[19573:866825] 9.4.0 - [FirebaseCore][I-COR000005] No app has been configured yet.
2022-09-02 12:02:26.836889+0300 Runner[19573:866830] 9.4.0 - [FirebaseCore][I-COR000005] No app has been configured yet.
2022-09-02 12:02:26.912218+0300 Runner[19573:866823] 9.4.0 - [FirebaseMessaging][I-FCM001000] FIRMessaging Remote Notifications proxy enabled, will swizzle remote notification receiver handlers. If you'd prefer to manually integrate Firebase Messaging, add "FirebaseAppDelegateProxyEnabled" to your Info.plist, and set it to NO. Follow the instructions at:
https://firebase.google.com/docs/cloud-messaging/ios/client#method_swizzling_in_firebase_messaging
to ensure proper integration.
2022-09-02 12:02:26.922498+0300 Runner[19573:866833] flutter: Initialized default app FirebaseApp([DEFAULT])
2022-09-02 12:02:26.922884+0300 Runner[19573:866823] 9.4.0 - [FirebaseMessaging][I-FCM002022] APNS device token not set before retrieving FCM Token for Sender ID '204532241696'. Notifications to this FCM Token will not be delivered over APNS.Be sure to re-retrieve the FCM token once the APNS device token is set.
2022-09-02 12:02:27.019553+0300 Runner[19573:866833] flutter: User granted permission
2022-09-02 12:02:29.000587+0300 Runner[19573:866833] flutter: The token is c1P2bEEh9Et3njcfkiOntp:APA91bFZIAR4oxrrwi0aJuYVofPC93x6wjy39TCTUt8-w6PkxtDgKTQUv-iQamhnd_pox6q2mm4JvBD6hSBS3bWmbzp8BO0LhpR89PvMwJv5ZdPWokguVkpMW6t5YUdIfVPwhlfTpsY5
2022-09-02 12:02:38.677058+0300 Runner[19573:866833] flutter: The notification is Instance of 'PushNotification'
2022-09-02 12:02:38.681029+0300 Runner[19573:866833] [VERBOSE-2:ui_dart_state.cc(198)] Unhandled Exception: 'package:overlay_support/src/overlay_state_finder.dart': Failed assertion: line 12 pos 7: '_debugInitialized': Global OverlaySupport Not Initialized !
ensure your app wrapped widget OverlaySupport.global
#0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:51:61)
#1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5)
#2 findOverlayState (package:overlay_support/src/overlay_state_finder.dart:12:7)
#3 showOverlay (package:overlay_support/src/overlay.dart:63:26)
#4 showOverlayNotification (package:overlay_support/src/notification/overlay_notification.dart:21:10)
#5 showSimpleNotification (package:overlay_support/src/notification/overlay_notification.dart:97:17)
#6 _RestartWidgetState.requestAndRegisterNotification.<anonymous closure> (package:medicte/main.dart:106:11)
#7 _rootRunUnary (dart:async/zone.dart:1434:47)
#8 _CustomZone.runUnary (dart:async/zone.dart:1335:19)
#9 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1244:7)
#10 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:341:11)
#11 _DelayedData.perform (dart:async/stream_impl.dart:591:14)
#12 _StreamImplEvents.handleNext (dart:async/stream_impl.dart:706:11)
#13 _PendingEvents.schedule.<anonymous closure> (dart:async/stream_impl.dart:663:7)
#14 _rootRun (dart:async/zone.dart:1418:47)
#15 _CustomZone.run (dart:async/zone.dart:1328:19)
#16 _CustomZone.runGuarded (dart:async/zone.dart:1236:7)
#17 _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1276:23)
#18 _rootRun (dart:async/zone.dart:1426:13)
#19 _CustomZone.run (dart:async/zone.dart:1328:19)
#20 _CustomZone.runGuarded (dart:async/zone.dart:1236:7)
#21 _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1276:23)
#22 _microtaskLoop (dart:async/schedule_microtask.dart:40:21)
#23 _startMicrotaskLoop (dart:async/schedule_microtask.dart:49:5)
Actually, error is Could not locate configuration file: 'GoogleService-Info.plist'.
If you didn't initialize the Firebase project, start with and follow this doc
firebase init
After all done, you will find

Use SDKOptions to Set Programmtically HERE Credentials and Cache Path

I want to get the HERE credentials from a web service and not from the android manifest or info.plist as stated in the HERE documentation, but at this point I get an error.
Here Flutter SDK version 4.8.0.0
import 'package:flutter/material.dart';
import 'package:here_sdk/core.dart';
import 'package:here_sdk/core.engine.dart';
import 'package:here_sdk/core.errors.dart';
import 'package:here_sdk/mapview.dart';
void main() {
SDKOptions sdkOptions = SDKOptions.withAccessKeySecretAndCachePath(
"YOUR_ACCESS_KEY_ID", "YOUR_ACCESS_KEY_SECRET", "");
SDKNativeEngine sdkNativeEngine;
try {
sdkNativeEngine = SDKNativeEngine(sdkOptions);
SDKNativeEngine.sharedInstance = sdkNativeEngine;
} on InstantiationException {
// Handle exception.
}
SdkContext.init(IsolateOrigin.main);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'HERE SDK for Flutter - Hello Map!',
home: HereMap(onMapCreated: _onMapCreated),
);
}
void _onMapCreated(HereMapController hereMapController) {
hereMapController.mapScene.loadSceneForMapScheme(MapScheme.normalDay,
(MapError? error) {
if (error != null) {
print('Map scene not loaded. MapError: ${error.toString()}');
return;
}
const double distanceToEarthInMeters = 8000;
hereMapController.camera.lookAtPointWithDistance(
GeoCoordinates(52.530932, 13.384915), distanceToEarthInMeters);
});
}
}
An error occurred
E/flutter (16773): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: Invalid argument(s): Failed to resolve an FFI function. Perhaps `LibraryContext.init()` was not called.
E/flutter (16773): Failed to lookup symbol (undefined symbol: here_sdk_sdk_core_engine_SDKOptions_make__String_String_String)
E/flutter (16773): #0 catchArgumentError (package:here_sdk/src/_library_context.dart:39:5)
E/flutter (16773): #1 SDKOptions._withAccessKeySecretAndCachePath (package:here_sdk/src/sdk/core/engine/s_d_k_options.dart:207:49)
E/flutter (16773): #2 new SDKOptions.withAccessKeySecretAndCachePath (package:here_sdk/src/sdk/core/engine/s_d_k_options.dart:94:121)
E/flutter (16773): #3 main (package:here_example/main.dart:8:38)
E/flutter (16773): #4 _runMainZoned.<anonymous closure>.<anonymous closure> (dart:ui/hooks.dart:142:25)
E/flutter (16773): #5 _rootRun (dart:async/zone.dart:1354:13)
How can I implement LibraryContext.init()?
Can you share code snippet about that?
Thanks.
Make sure to init the SDKContext before initializing the HERE SDK. I wish this would have been stated in the documentation, but apparently it is not.
So, instead of calling it after creating the SDKNativeEngine, call it like so:
void main() {
SdkContext.init(IsolateOrigin.main);
// Clear the cache occupied by a previous instance.
await SDKNativeEngine.sharedInstance?.dispose();
SDKOptions sdkOptions = SDKOptions.withAccessKeySecretAndCachePath(
"YOUR_ACCESS_KEY_ID", "YOUR_ACCESS_KEY_SECRET", "");
SDKNativeEngine sdkNativeEngine;
try {
sdkNativeEngine = SDKNativeEngine(sdkOptions);
SDKNativeEngine.sharedInstance = sdkNativeEngine;
} on InstantiationException {
// Handle exception.
}
runApp(MyApp());
}

Flutter/Dart - Unhandled Exception: MissingPluginException

I'm trying to access the device microphone on press with the aim of recording a voice-note. I have tried to access the current permission value but get the following error:
[VERBOSE-2:ui_dart_state.cc(166)] Unhandled Exception: MissingPluginException(No implementation found for method checkPermissionStatus on channel flutter.baseflow.com/permissions/methods)
#0 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:159:7)
<asynchronous suspension>
#1 MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:334:12)
#2 MethodChannelPermissionHandler.checkPermissionStatus (package:permission_handler_platform_interface/src/method_channel/method_channel_permission_handler.dart:15:41)
#3 PermissionActions.status (package:permission_handler/permission_handler.dart:30:51)
#4 _InsideLeftState.build.<anonymous closure> (package:easy_tiger/screens/order_card_flow/insideleft.dart:19:62)
#5 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:184:24)
#6 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:524:11)
#7 BaseTapGestureRecognizer._checkUp (pa<…>
[VERBOSE-2:profiler_metrics_ios.mm(184)] Error retrieving thread information: (ipc/send) invalid destination port
Here is the code for my screen:
import 'package:flutter/material.dart';
import 'package:audio_recorder/audio_recorder.dart';
import 'package:permission_handler/permission_handler.dart';
class InsideLeft extends StatefulWidget {
#override
_InsideLeftState createState() => _InsideLeftState();
}
class _InsideLeftState extends State<InsideLeft> {
#override
Widget build(BuildContext context) {
return Container(
child: GestureDetector(
child: Icon(Icons.mic),
onTap: () async {
PermissionStatus mic = await Permission.microphone.status;
print('microphone permission? ${mic.toString()}');
// try {
//// if (mic != PermissionStatus.granted) {
//// await Permission.microphone.request();
//// }
//// } catch (e) {
//// print(e);
//// }
},
),
);
}
}
Any thoughts how to get around this?
can you please try these things :
Migrate to androidX using android Studio
delete gradle caches
Good post about this topic: Flutter MissingPluginException with several plugins

Operations on File object never finishing during unit tests with Flutter

I'm writing unit tests for my Flutter app and I'm having some trouble when reading a json file. I have some json files that are used by my api mock class. Whenever I call a method on the File object, it never ends, but the test continues to execute, ending in an error because it didn't receive the data from the file object. It's very weird because it only happens when the mock api method is called from a provider that is called from a widget. If I call the mock api method directly my test, it works fine.
The structure of my app is as follows: WidgetScreen calls -> provider calls -> mockApi.
The Widget LegislacaoScreen calls the method carregarLegislacao from my provider, that in turn calls the method getLegislacoes from my mockApi, that method reads from a json file and returns the results to the provider that then notifies my widget. Any help is appreciated.
Oh and I'm running my tests with the command: flutter test test/my_test.dart
Here some code:
LegislacaoScreen:
class LegislacaoScreen extends StatefulWidget {
#override
_LegislacaoScreenState createState() => _LegislacaoScreenState();
}
class _LegislacaoScreenState extends State<LegislacaoScreen>
with AutomaticKeepAliveClientMixin {
#override
void initState() {
super.initState();
Future.microtask(carregarDados);
}
#override
bool get wantKeepAlive => true;
Future<void> carregarDados({bool reset = false}) async {
try {
final legislacaoProvider =
Provider.of<LegislacaoProvider>(context, listen: false);
await legislacaoProvider.carregarLegislacao(
page: legislacaoProvider.currentPage + 1,
reset: reset,
);
} catch (err) {}
}
Widget renderBody(legislacaoProvider) {
if (legislacaoProvider.carregandoLegislacao &&
legislacaoProvider.legislacoes.isEmpty &&
!legislacaoProvider.reseting) {
return Center(
child: CircularProgressIndicator(),
);
}
if (legislacaoProvider.erroCarregandoLegislacao) {
return MensagemAcao(
mensagem:
'Houve um erro ao carregar os dados.\nPor favor tente novamente.',
acao: () => carregarDados(reset: true),
);
}
return LegislacaoLista(
carregandoLegislacao: legislacaoProvider.carregandoLegislacao,
legislacoes: legislacaoProvider.legislacoes,
reseting: legislacaoProvider.reseting,
carregarDados: carregarDados,
isFiltroEmpty: legislacaoProvider.isFiltroEmpty,
);
}
#override
Widget build(BuildContext context) {
super.build(context);
final legislacaoProvider = Provider.of<LegislacaoProvider>(context);
return Scaffold(
appBar: CustomAppBar(
title: 'Legislação',
actions: <Widget>[
IconButton(
icon: Icon(
Icons.search,
color: legislacaoProvider.isFiltroEmpty
? Colors.white
: CustomColors.verde2,
),
onPressed: () {
Navigator.of(context)
.pushNamed(PesquisarLegislacaoScreen.routeName);
},
),
],
),
backgroundColor: Colors.white,
body: renderBody(legislacaoProvider),
);
}
Provider:
class LegislacaoProvider with ChangeNotifier {
bool _carregandoLegislacao = true;
bool _erroCarregandoLegislacao = false;
List<Legislacao> legislacoes = [];
int currentPage = 0;
bool _reseting = false;
LegislacaoApi _legislacaoApi;
LegislacaoProvider({LegislacaoApi legislacaoApi})
: _legislacaoApi = legislacaoApi ?? LegislacaoApiService();
bool get carregandoLegislacao {
return _carregandoLegislacao;
}
bool get erroCarregandoLegislacao {
return _erroCarregandoLegislacao;
}
bool get reseting {
return _reseting;
}
Future<void> carregarLegislacao({
int page = 1,
bool reset = false,
}) async {
try {
_carregandoLegislacao = true;
_erroCarregandoLegislacao = false;
_reseting = reset;
if (reset) {
page = 1;
}
notifyListeners();
legislacoes = await _legislacaoApi.getLegislacoes(
page: page,
ementa: filtro['ementa'],
conteudo: filtro['conteudo'],
ano: filtro['ano'],
periodoInicialLegislacao: filtro['periodoInicialLegislacao'],
periodoFinalLegislacao: filtro['periodoFinalLegislacao'],
tipoLegislacao: filtro['tipoLegislacao']?.id,
numero: filtro['numero'],
autor: filtro['autor'],
);
_carregandoLegislacao = false;
_reseting = false;
notifyListeners();
} catch (err) {
print(err);
_erroCarregandoLegislacao = true;
_carregandoLegislacao = false;
_reseting = false;
notifyListeners();
throw TaNaLeiException(err.toString());
}
}
}
MockApi:
class LegislacaoApiServiceMock extends LegislacaoApi {
bool _fail = false;
LegislacaoApiServiceMock();
LegislacaoApiServiceMock.fail() : _fail = true;
#override
Future<List<Legislacao>> getLegislacoes({
int page = 1,
String conteudo,
String ementa,
String ano,
DateTime periodoInicialLegislacao,
DateTime periodoFinalLegislacao,
int tipoLegislacao,
String numero,
String autor,
}) async {
if (_fail) {
throw Error();
}
final file = File('test/data/get_legislacoes.json');
print('step 1');
final json = await file.readAsString();
print('step 2 '); // <-- NEVER GETS PRINTED
return jsonDecode(json)
.map<Legislacao>(
(elemento) => Legislacao.fromJson(elemento),
)
.toList();
}
}
And finally, my tests:
final locator = GetIt.instance;
void setupLocatorLegislacaoProvider({LegislacaoApi legislacaoApi}) {
locator.registerLazySingleton<LegislacaoProvider>(() => LegislacaoProvider(
legislacaoApi: legislacaoApi,
));
}
void main() async {
TestWidgetsFlutterBinding.ensureInitialized();
setupLocatorLegislacaoProvider(legislacaoApi: LegislacaoApiServiceMock());
group('LegislacaoScreen', () {
Widget buildWidget() {
return ChangeNotifierProvider.value(
value: locator<LegislacaoProvider>();,
child: MaterialApp(
home: LegislacaoScreen(),
routes: routes,
),
);
}
testWidgets('builds screen', (WidgetTester tester) async {
final child = buildWidget();
await tester.pumpWidget(child);
expect(find.byWidget(child), findsOneWidget);
});
testWidgets('finds list when data is loaded',
(WidgetTester tester) async {
await tester.pumpWidget(buildWidget());
await tester.pump();
await tester.pump();
expect(find.byType(LegislacaoLista), findsOneWidget);
});
});
}
EDIT:
Here is the error message from running the tests:
➜ ta_na_lei git:(testes) ✗ flutter test test/ui/screens/legislacao/legislacao_screen_test.dart
00:03 +0: LegislacaoScreen builds screen
dentro de mock 1
00:04 +1: LegislacaoScreen exibe circular progress indicator quando carregando legislacao e reset true
dentro de mock 1
00:04 +2: LegislacaoScreen exibe lista de legislacao quando legislacoes carregadas
dentro de mock 1
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure object was thrown running a test:
Expected: exactly one matching node in the widget tree
Actual: _WidgetTypeFinder:<zero widgets with type "LegislacaoLista" (ignoring offstage widgets)>
Which: means none were found but one was expected
When the exception was thrown, this was the stack:
#4 main.<anonymous closure>.<anonymous closure> (file:///Users/home/Projetos/casacivil/ta_na_lei/test/ui/screens/legislacao/legislacao_screen_test.dart:49:7)
<asynchronous suspension>
#5 main.<anonymous closure>.<anonymous closure> (file:///Users/home/Projetos/casacivil/ta_na_lei/test/ui/screens/legislacao/legislacao_screen_test.dart)
#6 testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:140:29)
<asynchronous suspension>
#7 testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart)
#8 TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:703:19)
<asynchronous suspension>
#11 TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:683:14)
#12 AutomatedTestWidgetsFlutterBinding.runTest.<anonymous closure> (package:flutter_test/src/binding.dart:1083:24)
#18 AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:1080:15)
#19 testWidgets.<anonymous closure> (package:flutter_test/src/widget_tester.dart:133:24)
#20 Declarer.test.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:172:27)
<asynchronous suspension>
#21 Declarer.test.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart)
#22 Invoker.waitForOutstandingCallbacks.<anonymous closure> (package:test_api/src/backend/invoker.dart:246:15)
#27 Invoker.waitForOutstandingCallbacks (package:test_api/src/backend/invoker.dart:243:5)
#28 Declarer.test.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:170:33)
#33 Declarer.test.<anonymous closure> (package:test_api/src/backend/declarer.dart:169:13)
#34 Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart:400:30)
(elided 31 frames from class _FakeAsync, class _RawReceivePortImpl, class _Timer, dart:async, dart:async-patch, and package:stack_trace)
This was caught by the test expectation on the following line:
file:///Users/home/Projetos/casacivil/ta_na_lei/test/ui/screens/legislacao/legislacao_screen_test.dart line 49
The test description was:
exibe lista de legislacao quando legislacoes carregadas
════════════════════════════════════════════════════════════════════════════════════════════════════
00:04 +2 -1: LegislacaoScreen exibe lista de legislacao quando legislacoes carregadas [E]
Test failed. See exception logs above.
The test description was: exibe lista de legislacao quando legislacoes carregadas
00:04 +3 -1: Some tests failed.
EDIT 2:
It seems the issue is with the async environment, after I read this article https://medium.com/flutter/event-loop-in-widget-tester-50b3ca5e9fc5 . I tried to call my test inside a runAsync method, it did not work, then I added await Future.delayed(Duration(seconds: 5)) and It worked. For some reason its taking a long time to read the file (In other tests it reads really fast). One interesting thing is that pump with a duration does not delay the tests...
Well, it works now with this workaround, but I'd like to know the correct way to fix the issue.
await tester.runAsync(() async {
await tester.pumpWidget(buildWidget());
await Future.delayed(Duration(seconds: 5));
await tester.pump(Duration(seconds: 2));
expect(find.byType(LegislacaoLista), findsOneWidget);
});

Flutter Native Admob - MissingPluginException

I am trying to add native ads using the plugin flutter_native_admob but I always get this error:
E/flutter ( 6504): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: MissingPluginException(No implementation found for method initialize on channel flutter_native_admob)
E/flutter ( 6504): #0 MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:319:7)
E/flutter ( 6504): <asynchronous suspension>
E/flutter ( 6504): #1 NativeAdmob.initialize (package:flutter_native_admob/flutter_native_admob.dart:105:20)
E/flutter ( 6504): #2 HomePageState.initState (package:spanglishcards/view/card/HomePage.dart:67:19)
E/flutter ( 6504): #3 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4355:58)
I tried to add it in my Home page:
class HomePage extends StatefulWidget {
HomePage({
this.user,
});
final User user;
#override
State<StatefulWidget> createState() => HomePageState();
}
class HomePageState extends State<HomePage> {
final _nativeAdmobs = NativeAdmob();
Future initState() {
super.initState();
_nativeAdmobs.initialize(appID:"ca-app-pub-XXX~XXX" );
}
}
And also in the main.dart:
final _nativeAdmobs = NativeAdmob();
void main(){
WidgetsFlutterBinding.ensureInitialized();
_nativeAdmobs.initialize(appID:"ca-app-pub-XXX~XXX" );
runApp(MyApp());
}
I also tried to run flutter clean / flutter run before just in case, but it doesn't work either. Any solution?
To discard issues with my appID, the plugin firebase_admob (doesn't support native ads) works.