Operations on File object never finishing during unit tests with Flutter - 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);
});

Related

keep getting "Null check operator used on a null value" error. i've checked my code 100 times

keep getting "Null check operator used on a null value" error
actually im trying to use providers to get values from database and use em in my app
but im stuck at this i checked my code in every single way but still i keep getting this error
:lib\providers\user_provider.dart
import 'package:flutter/widgets.dart';
import 'package:test_app/models/user.dart';
import 'package:test_app/resources/auth_methods.dart';
class UserProvider with ChangeNotifier {
User? _user;
final AuthMethods _authMethods = AuthMethods();
User get getUser => _user!;
Future<void> refreshUser() async {
User user = await _authMethods.getUserDetails();
_user = user;
notifyListeners();
}
}
lib\pages\home.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_app/providers/user_provider.dart';
import 'package:test_app/models/user.dart' as model;
// import 'dart:html';
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
void initState() {
super.initState();
addData();
}
addData() async {
UserProvider _userProvider =
Provider.of<UserProvider>(context, listen: false);
await _userProvider.refreshUser();
}
#override
Widget build(BuildContext context) {
model.User user = Provider.of<UserProvider>(context).getUser;
return Scaffold(
body: Center(
child: Text("this is homepage"),
),
);
}
}
debug snippet
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following _CastError was thrown building Home(dirty, dependencies:
[_InheritedProviderScope<UserProvider?>], state: _HomeState#17895):
Null check operator used on a null value
The relevant error-causing widget was:
Home Home:file:///C:/Users/abc/Desktop/flutter/test_app/lib/main.dart:53:34
When the exception was thrown, this was the stack:
#0 UserProvider.getUser (package:test_app/providers/user_provider.dart:9:28)
#1 _HomeState.build (package:test_app/pages/home.dart:36:59)
#2 StatefulElement.build (package:flutter/src/widgets/framework.dart:4870:27)
#3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4754:15)
#4 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4928:11)
#5 Element.rebuild (package:flutter/src/widgets/framework.dart:4477:5)
#6 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:4735:5)
#7 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4919:11)
#8 ComponentElement.mount (package:flutter/src/widgets/framework.dart:4729:5)
#9 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3790:14)
#10 Element.updateChild (package:flutter/src/widgets/framework.dart:3524:20)
#11 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4780:16)
#12 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4928:11)
#13 Element.rebuild (package:flutter/src/widgets/framework.dart:4477:5)
#14 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2659:19)
#15 WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:882:21)
#16 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:363:5)
#17 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1144:15)
#18 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1081:9)
#19 SchedulerBinding.scheduleWarmUpFrame.<anonymous closure> (package:flutter/src/scheduler/binding.dart:862:7)
(elided 11 frames from class _RawReceivePortImpl, class _Timer, dart:async, and dart:async-patch)
idk what the problem actually im new to flutter and dart
i've using python for my whole life
refreshUser is a future and it sets _user but because you're calling getUser IMMEDIATELY build method, which basically returns _user as a non-null value, the error would be thrown.
Try this:
class UserProvider with ChangeNotifier {
User? _user;
final AuthMethods _authMethods = AuthMethods();
User? get getUser => _user;
Future<void> refreshUser() async {
User user = await _authMethods.getUserDetails();
_user = user;
notifyListeners();
}
}
And your _HomeState:
class _HomeState extends State<Home> {
#override
void initState() {
super.initState();
addData();
}
addData() async {
UserProvider _userProvider =
Provider.of<UserProvider>(context, listen: false);
await _userProvider.refreshUser();
}
#override
Widget build(BuildContext context) {
model.User? user = Provider.of<UserProvider>(context).getUser;
return Scaffold(
body: Center(
child: Text("this is homepage"),
),
);
}
}
In User_Provider.dart,
instead of doing:
User get getUser => _user!;
Do this:
User? get getUser => _user;

Google book api in postman working fine But in flutter giving status code 404

When I run google book API in postman It is working fine and Giving status code 200. But In flutter, it gives the following error. And Giving 404 status code.
I have generated API Key and also I have enabled Google Book API.
So please help me If anyone knows what I'm doing wrong.
TestLayout.dart
class TestLayout extends StatefulWidget {
static String id = "Test_Layout";
#override
_TestLayoutState createState() => _TestLayoutState();
}
class _TestLayoutState extends State<TestLayout> {
Future<void> getData() async {
String ApiKey = "..........";
final response = await http.get(
Uri.https("books.googleapis.com",
"books/v1/volumes?q=flowers+inauthor:keyes&key=$ApiKey"),
headers: {"Content-Type": "application/json"});
var jsonBody = jsonDecode(response.body);
//book b1=book.fromJson(jsonBody);
print(response.statusCode);
print(b1.id);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Flutter"),
),
body: Column(
children: [
TextButton(
onPressed: () async {
await getData();
},
child: Text("Submit")),
],
),
);
}
}
Book.dart:
class book {
final int id;
book({this.id});
factory book.fromJson(Map<String,dynamic> json)
{
return book(
id: json["totalItems"]
);
}
}
Error:
E/flutter (27854): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception:
FormatException: Unexpected character (at character 1)
E/flutter (27854): <!DOCTYPE html>
E/flutter (27854): ^
E/flutter (27854):
E/flutter (27854): #0 _ChunkedJsonParser.fail (dart:convert-
patch/convert_patch.dart:1404:5)
E/flutter (27854): #1 _ChunkedJsonParser.parseNumber (dart:convert-
patch/convert_patch.dart:1271:9)
E/flutter (27854): #2 _ChunkedJsonParser.parse (dart:convert-
patch/convert_patch.dart:936:22)
E/flutter (27854): #3 _parseJson (dart:convert-patch/convert_patch.dart:40:10)
E/flutter (27854): #4 JsonDecoder.convert (dart:convert/json.dart:506:36)
E/flutter (27854): #5 JsonCodec.decode (dart:convert/json.dart:157:41)
E/flutter (27854): #6 jsonDecode (dart:convert/json.dart:96:10)
E/flutter (27854): #7 _TestLayoutState.getData
(package:freebookshare/TestFile/TestLayout.dart:22:20)
E/flutter (27854): <asynchronous suspension>
E/flutter (27854): #8 _TestLayoutState.build.<anonymous closure>
(package:freebookshare/TestFile/TestLayout.dart:40:17)
E/flutter (27854): <asynchronous suspension>
E/flutter (27854):
I Used Uri.parse() method and it worked.
String url="https://books.googleapis.com/books/v1/volumes?q=flowers+inauthor:keyes&key=$ApiKey";
final response = await http.get(Uri.parse(url),
headers: {"Content-Type": "application/json"});
This was an old issue that I also had previously. After digging some research, I got my necessary output by removing the Uri Constructor in my API call. Try to remove Uri Constructor and add the code for the final response as follows
final response = await http.get("https://www.googleapis.com/books/v1/volumes?q=flowers+inauthor:keyes&key=$apiKey",
headers: {"Content-Type": "application/json"});
After this, the code of TestLayout will be as follows
class TestLayout extends StatefulWidget {
static String id = "Test_Layout";
#override
_TestLayoutState createState() => _TestLayoutState();
}
class _TestLayoutState extends State<TestLayout> {
Future<void> getData() async {
String apiKey = "..........";
final response = await http.get("https://www.googleapis.com/books/v1/volumes?q=flowers+inauthor:keyes&key=$apiKey",
headers: {"Content-Type": "application/json"});
var jsonBody = jsonDecode(response.body);
book b1=book.fromJson(jsonBody);
print(response.statusCode);
print(b1.id);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Flutter"),
),
body: Column(
children: [
TextButton(
onPressed: () async {
await getData();
},
child: Text("Submit")),
],
),
);
}
}
Hope this will work for you

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

Flutter tester..tap not working. How to solve "Bad state: No element" error?

I'm writing a widget test in my Flutter app. I'able to find the button using key or text or widget type but when I tap it, it gives Bad State No element error. Below is my test source code. I can also see the element in tester object while debugging under allWidgets item.
class MockSpeechRecognizer extends Mock implements SpeechRecognizer {}
void main() {
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
setUpAll(() {
const MethodChannel('plugins.flutter.io/shared_preferences')
.setMockMethodCallHandler((MethodCall methodCall) async {
if (methodCall.method == 'getAll') {
return <String, dynamic>{}; // set initial values here if desired
}
return null;
});
setupLocator();
});
group('DashboardView Test | ', () {
testWidgets('Build Dashboard view and change keyword', (WidgetTester tester) async {
final Preferences preferences=Preferences();
preferences.keyword='hello';
final MockSpeechRecognizer speechService = MockSpeechRecognizer();
when(speechService.isServiceRunning).thenReturn(true);
locator.unregister<SpeechRecognizer>();
locator.registerLazySingleton<SpeechRecognizer>(() => speechService);
locator<LocalStorageService>().setUserPreferences(preferences);
// Build our app and trigger a frame.
binding.window.physicalSizeTestValue = Size(600, 300);
await tester.pumpWidget(MaterialApp(home:Dashboard()));
await tester.pumpAndSettle();
expect(find.byType(Dashboard),findsOneWidget);
await tester.tap(find.byKey(const Key('ChangeKeyword')));
});
});
}
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following StateError was thrown running a test:
Bad state: No element
When the exception was thrown, this was the stack:
#0 Iterable.single (dart:core/iterable.dart:554:25)
#1 WidgetController._getElementPoint (package:flutter_test/src/controller.dart:646:47)
#2 WidgetController.getCenter (package:flutter_test/src/controller.dart:618:12)
#3 WidgetController.tap (package:flutter_test/src/controller.dart:256:18)
#4 main.<anonymous closure>.<anonymous closure> (file:///E:/Siak/Meow/meow-phone-finder/test/Widgets/dashboardView_test.dart:52:20)
<asynchronous suspension>
#5 main.<anonymous closure>.<anonymous closure> (file:///E:/Siak/Meow/meow-phone-finder/test/Widgets/dashboardView_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
Where do you define the Button, have you set the Key parameter?
await tester.pumpWidget(MaterialApp(home:
Container(child:
Button(
key: Key("ChangeKeyword"),
onTap: () => {},
child: Text("Press me"),
),
),
));

Screen not loading after login is authenticated in flutter

I have created a route to the home screen which should be displayed after login is successful. However, my circular bar just keeps moving (indicating isLoading stage) and the home screen does not show up in the simulator.
I am aware that the login is successful since my response variable does print the information in the console that it accesses after login.
On pressing the login the button 2 main issues are displayed:
1) Exception thrown while handling a gesture
2) Unhandled Exception: Failed assertion: boolean expression must not be null
I am unable to solve this these 2 issues. Any help would be great.
Thanks in advance!
There is no compilation error. The following are the main issues shown after pressing the login button (complete stack tree and message posted later in the question):
flutter: The following ArgumentError was thrown while handling a gesture:
flutter: Invalid argument (onError): Error handler must accept one Object or one Object and a StackTrace as
flutter: arguments, and return a a valid result: Closure: (Exception) => void
Unhandled Exception: Failed assertion: boolean expression must not be null
The console message posted later refers to 3 files. I have posted important code of the files below.
File: login_screen.dart
import 'dart:ui';
import 'package:flutter/material.dart';
class LoginScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return new LoginScreenState();
}
}
class LoginScreenState extends State<LoginScreen>
implements LoginScreenContract, AuthStateListener {
BuildContext _ctx;
bool _isLoading = false;
final formKey = new GlobalKey<FormState>();
final scaffoldKey = new GlobalKey<ScaffoldState>();
String _password, _username;
LoginScreenPresenter _presenter;
LoginScreenState() {
_presenter = new LoginScreenPresenter(this);
var authStateProvider = new AuthStateProvider();
authStateProvider.subscribe(this);
}
void _submit() {
final form = formKey.currentState;
if (form.validate()) {
setState(() => _isLoading = true);
form.save();
_presenter.doLogin(_username, _password);
}
}
void _showSnackBar(String text) {
scaffoldKey.currentState
.showSnackBar(new SnackBar(content: new Text(text)));
}
#override
onAuthStateChanged(AuthState state) {
if(state == AuthState.LOGGED_IN)
Navigator.of(_ctx).pushReplacementNamed("/home");
}
#override
Widget build(BuildContext context) {
_ctx = context;
....(UI for login screen)
#override
void onLoginError(String errorTxt) {
_showSnackBar(errorTxt);
setState(() => _isLoading = false);
}
#override
void onLoginSuccess(User user) async {
HomeScreen()));
_showSnackBar(user.toString());
setState(() => _isLoading = false);
var db = new DatabaseHelper();
await db.saveUser(user);
var authStateProvider = new AuthStateProvider();
HomeScreen()));
authStateProvider.notify(AuthState.LOGGED_IN);
onAuthStateChanged(AuthState.LOGGED_IN);
}
}
File: login_screen_presenter.dart
import 'package:better_login/rest_ds.dart';
import 'package:better_login/user.dart';
abstract class LoginScreenContract {
void onLoginSuccess(User user);
void onLoginError(String errorTxt);
}
class LoginScreenPresenter {
LoginScreenContract _view;
RestDatasource api = new RestDatasource();
LoginScreenPresenter(this._view);
doLogin(String username, String password) {
api.login(username, password).then((User user) {
_view.onLoginSuccess(user);
}).catchError((Exception error) =>
_view.onLoginError(error.toString()));
}
}
File: rest_ds.dart
static final LOGIN_URL = "https://legacy- api.example.com/v1/auth/login";
Future<User> login(String username, String password) {
return _netUtil.post(LOGIN_URL, body: {
"username": username,
"password": password
}).then((dynamic res) {
print(res.toString());
if(res["error"]){ throw new Exception(res["error_msg"]);}
return new User.map(res["user"]);
});
}
}
This is the complete info displayed in the console:
flutter: The following ArgumentError was thrown while handling a gesture:
flutter: Invalid argument (onError): Error handler must accept one Object or one Object and a StackTrace as
flutter: arguments, and return a a valid result: Closure: (Exception) => void
flutter:
flutter: When the exception was thrown, this was the stack:
flutter: #2 LoginScreenPresenter.doLogin (package:better_login/login_screen_presenter.dart:17:8)
flutter: #3 LoginScreenState._submit (package:better_login/login_screen.dart:41:18)
flutter: #4 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:511:14)
flutter: #5 _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:566:30)
flutter: #6 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:166:24)
flutter: #7 TapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:240:9)
flutter: #8 TapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:177:9)
flutter: #9 PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:436:9)
flutter: #10 PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:73:12)
flutter: #11 PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:101:11)
flutter: #12 _WidgetsFlutterBinding&BindingBase&GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:221:19)
flutter: #13 _WidgetsFlutterBinding&BindingBase&GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:199:22)
flutter: #14 _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:156:7)
flutter: #15 _WidgetsFlutterBinding&BindingBase&GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:102:7)
flutter: #16 _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:86:7)
flutter: #20 _invoke1 (dart:ui/hooks.dart:233:10)
flutter: #21 _dispatchPointerDataPacket (dart:ui/hooks.dart:154:5)
flutter: (elided 5 frames from package dart:async)
flutter:
flutter: Handler: onTap
flutter: Recognizer:
flutter: TapGestureRecognizer#398b8(debugOwner: GestureDetector, state: possible, won arena, finalPosition:
flutter: Offset(194.0, 504.0), sent tap down)
flutter: ════════════════════════════════════════════════════════════════════════════════════════════════════
flutter: {status: success, message: Authentication Success, executionTime: 0.052, data: {token: U2FsdGVkX19ZQS5wQXRYWTkh2o4PyrtmhS4kELJO0WsEBDbn30G9Oig/13fzHzqZ, custKey: anb2mNXFERnJ4IQW....(rest is info got on login)
[VERBOSE-2:ui_dart_state.cc(148)] Unhandled Exception: Failed assertion: boolean expression must not be null
#0 RestDatasource.login.<anonymous closure> (package:better_login/rest_ds.dart:21:13)
#1 _rootRunUnary (dart:async/zone.dart:1132:38)
#2 _CustomZone.runUnary (dart:async/zone.dart:1029:19)
#3 _FutureListener.handleValue (dart:async/future_impl.dart:126:18)
#4 Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:639:45)
#5 Future._propagateToListeners (dart:async/future_impl.dart:668:32)
#6 Future._complete (dart:async/future_impl.dart:473:7)
#7 _SyncCompleter.complete (dart:async/future_impl.dart:51:12)
#8 _AsyncAwaitCompleter.complete (dart:async-patch/async_patch.dart:28:18)
#9 _completeOnAsyncReturn (dart:async-patch/async_patch.dart:294:13)
#10 _withClient (package:http/http.dart)
<asynchronous suspension>
#11 post (package:http/http.dart:69:5)
#12 NetworkUtil.post (package:better_login/network_util.dart<…>
There are several issues in your code:
catchError param is not of type Exception is of type Object. You can use the second param of catchError, test to filter errors received. This is the cause of the crash report trace The following ArgumentError was thrown while handling a gestur.
the main cause of the crash is here if(res["error"]){ throw new Exception(res["error_msg"]);}, because the res["error"] is probably null. To fix it change it to if(res["error"] != null) .....
Refactoring points:
you don't need to save the build context in a _ctx variable, any subclass of State has access to it's main context via context property.