How to fix my code to show alert dialog on flutter? - dart

I have an app which exports a json object to a json file and while it's exporting, I wanted to show an alert dialog with a circular progress indicator on it. But for some reason, the alert dialog with my progress indicator is not showing up.
This is the look of my app before I export my json:
Here is the code for activating the exporting part:
...
child: FlatButton(
onPressed: () async{
//Popping the confirm dialog
Navigator.pop(context);
//Showing the progress dialog
showProcessingDialog();
//Buying some time
_timer = Timer(Duration(seconds: 5), exportData);
//Pops the progress dialog
Navigator.pop(context);
//Shows the finished dialog
showFinishedDialog();
},
child: Text(
"Yes",
...
After I click 'Yes' in this alert button, it should show the progress dialog but it doesn't show, instead it shows the finished dialog.
Like this:
Here is the code for progress dialog:
void showProcessingDialog() async{
return showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext context){
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0))),
contentPadding: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 0.0),
content: Container(
width: 250.0,
height: 100.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
CircularProgressIndicator(),
Text("Exporting...",
style: TextStyle(
fontFamily: "OpenSans",
color: Color(0xFF5B6978)
)
)
]
)
)
);
}
);
}
Here is the exportData callback:
void exportData() async{
List<dynamic> _msgList = await _msgStorage._getList;
await _expData._saveList(_msgList);
}
I have tried to add Timer class to delay showing finished dialog for 3 seconds but it doesn't work. I can confirm that my json file was exported successfully but the callback of Timer which is the progress dialog didn't show up.
I would appreciate any kind of help.
UPDATE:
I rewrote my code based on the answer of diegoveloper:
onPressed: () async{
Navigator.pop(context);
print("confirm dialog has pop");
print("showing processdialog");
showProcessingDialog();
print("processdialog is being shown.");
print("buying some time");
await Future.delayed(Duration(seconds: 5));
print("done buying some time");
print("exporting begin");
await exportData();
print("exporting done");
Navigator.pop(context);
print("processdialog has pop");
print("showing finished dialog");
showFinishedDialog();
print("finished dialog is being shown.");
},
At this point, the process dialog is being shown but after printing the "exporting done" and executing the Navigator.pop(context); it gave an error and the process dialog remains in the screen, unpopped.
Like this:
I/flutter ( 9767): confirm dialog has pop
I/flutter ( 9767): showing processdialog
I/flutter ( 9767): processdialog is being shown.
I/flutter ( 9767): buying some time
I/flutter ( 9767): done buying some time
I/flutter ( 9767): exporting begin
I/flutter ( 9767): exporting done
E/flutter ( 9767): [ERROR:flutter/shell/common/shell.cc(184)] Dart Error: Unhandled exception:
E/flutter ( 9767): Looking up a deactivated widget's ancestor is unsafe.
E/flutter ( 9767): At this point the state of the widget's element tree is no longer stable. To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling inheritFromWidgetOfExactType() in the widget's didChangeDependencies() method.
After I comment out the await Future.delayed(Duration(seconds: 5)); it worked fine.
My question is why did it failed when using Future.delayed?
Here is the full error:
I/flutter ( 9767): confirm dialog has pop
I/flutter ( 9767): showing processingdialog
I/flutter ( 9767): processdialog is being shown.
I/flutter ( 9767): buying some time
I/flutter ( 9767): done buying some time
I/flutter ( 9767): exporting begin
I/flutter ( 9767): exporting done
E/flutter ( 9767): [ERROR:flutter/shell/common/shell.cc(184)] Dart Error: Unhandled exception:
E/flutter ( 9767): Looking up a deactivated widget's ancestor is unsafe.
E/flutter ( 9767): At this point the state of the widget's element tree is no longer stable. To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling inheritFromWidgetOfExactType() in the widget's didChangeDependencies() method.
E/flutter ( 9767):
E/flutter ( 9767): #0 Element._debugCheckStateIsActiveForAncestorLookup.<anonymous closure> (package:flutter/src/widgets/framework.dart:3246:9)
E/flutter ( 9767): #1 Element._debugCheckStateIsActiveForAncestorLookup (package:flutter/src/widgets/framework.dart:3255:6)
E/flutter ( 9767): #2 Element.ancestorStateOfType (package:flutter/src/widgets/framework.dart:3303:12)
E/flutter ( 9767): #3 Navigator.of (package:flutter/src/widgets/navigator.dart:1288:19)
E/flutter ( 9767): #4 ChatWindow.showExportedDialog.<anonymous closure>.<anonymous closure> (package:msgdiary/main.dart:368:37)
E/flutter ( 9767): <asynchronous suspension>
E/flutter ( 9767): #5 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:507:14)
E/flutter ( 9767): #6 _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:562:30)
E/flutter ( 9767): #7 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:102:24)
E/flutter ( 9767): #8 TapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:242:9)
E/flutter ( 9767): #9 TapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:175:7)
E/flutter ( 9767): #10 PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:315:9)
E/flutter ( 9767): #11 PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:73:12)
E/flutter ( 9767): #12 PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:101:11)
E/flutter ( 9767): #13 _WidgetsFlutterBinding&BindingBase&GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:180:19)
E/flutter ( 9767): #14 _WidgetsFlutterBinding&BindingBase&GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:158:22)
E/flutter ( 9767): #15 _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:138:7)
E/flutter ( 9767): #16 _WidgetsFlutterBinding&BindingBase&GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:101:7)
E/flutter ( 9767): #17 _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:85:7)
E/flutter ( 9767): #18 _invoke1 (dart:ui/hooks.dart:168:13)
E/flutter ( 9767): #19 _dispatchPointerDataPacket (dart:ui/hooks.dart:122:5)
UPDATE:
It was my fault. I need to study more about context. It seems that I was popping the same context for the two dialogs. I changed the name of the dialog and it worked.

Why don't you extract it to a custom dialog widget and handle its states dynamically? It's cleaner and more customizable, also giving a timer (like you did of 5 seconds) it's not a good practice since you can't be sure how much time it will take to do its work.
Then I can suggest, for example, to create an enum DialogState with 3 states
enum DialogState {
LOADING,
COMPLETED,
DISMISSED,
}
Then create your own Dialog widget that when built receives its current state
class MyDialog extends StatelessWidget {
final DialogState state;
MyDialog({this.state});
#override
Widget build(BuildContext context) {
return state == DialogState.DISMISSED
? Container()
: AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(10.0),
),
),
content: Container(
width: 250.0,
height: 100.0,
child: state == DialogState.LOADING
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
Padding(
padding: const EdgeInsets.only(left: 10.0),
child: Text(
"Exporting...",
style: TextStyle(
fontFamily: "OpenSans",
color: Color(0xFF5B6978),
),
),
)
],
)
: Center(
child: Text('Data loaded with success'),
),
),
);
}
}
and then, in your screen, you can insert it anywhere you want. I changed my exportData function to dummy a request that takes 5 seconds.
class MyScreen extends StatefulWidget {
_MyScreenState createState() => _MyScreenState();
}
class _MyScreenState extends State<MyScreen> {
DialogState _dialogState = DialogState.DISMISSED;
void _exportData() {
setState(() => _dialogState = DialogState.LOADING);
Future.delayed(Duration(seconds: 5)).then((_) {
setState(() => _dialogState = DialogState.COMPLETED);
Timer(Duration(seconds: 3), () => setState(() => _dialogState = DialogState.DISMISSED));
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Stack(
alignment: Alignment.center,
children: <Widget>[
RaisedButton(
child: Text('Show dialog'),
onPressed: () => _exportData(),
),
MyDialog(
state: _dialogState,
)
],
),
),
);
}
}

You can do the following:
onPressed: () async{
//Popping the confirm dialog
Navigator.pop(context);
//Showing the progress dialog
showProcessingDialog();
//wait 5 seconds : just for testing purposes, you don't need to wait in a real scenario
await Future.delayed(Duration(seconds: 5));
//call export data
await exportData();
//Pops the progress dialog
Navigator.pop(context);
//Shows the finished dialog
await showFinishedDialog();
},

Related

Android Studio Navigator operation requested with a context that does not include a Navigator

I want to go to Second Page with Navigator.push method. So I am using ElevatedButton Widget for this purpose. I'm also using Statefull Widget. My source code like in below:
import 'dart:io' show Platform;
import 'package:flutter/material.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
import 'ScannerPage.dart';
void main() {
runApp(const QrCodeMainWindow());
}
class QrCodeMainWindow extends StatefulWidget {
const QrCodeMainWindow({Key? key}) : super(key: key);
#override
State<QrCodeMainWindow> createState() => _QrCodeMainWindowState();
}
class _QrCodeMainWindowState extends State<QrCodeMainWindow> {
final String _data = "";
QRViewController? controller;
Barcode? result;
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
#override
Widget build(BuildContext context) {
final ButtonStyle style = ElevatedButton.styleFrom(
textStyle: const TextStyle(fontSize: 20),
);
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('QR Code Scanner App'),
backgroundColor: Colors.blueAccent,
),
body: Column(
children: [
ElevatedButton(
child: Text('Scan'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ScannerPage()),
);
}),
Text(_data)
],
),
),
);
}
When I run this App succesfuly openning on simulator (Iphone 13). But when I press the button I get below error.
How Can I solve this error ?
======== Exception caught by gesture ===============================================================
The following assertion was thrown while handling a gesture:
Navigator operation requested with a context that does not include a Navigator.
The context used to push or pop routes from the Navigator must be that of a widget that is a descendant of a Navigator widget.
When the exception was thrown, this was the stack:
#0 Navigator.of.<anonymous closure> (package:flutter/src/widgets/navigator.dart:2553:9)
#1 Navigator.of (package:flutter/src/widgets/navigator.dart:2560:6)
#2 Navigator.push (package:flutter/src/widgets/navigator.dart:2016:22)
#3 _QrCodeMainWindowState.build.<anonymous closure> (package:qr_code_scanner_example/main.dart:51:29)
#4 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:989:21)
#5 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:198:24)
#6 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:608:11)
#7 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:296:5)
#8 BaseTapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:230:7)
#9 PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:563:9)
#10 PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:94:12)
#11 PointerRouter._dispatchEventToRoutes.<anonymous closure> (package:flutter/src/gestures/pointer_router.dart:139:9)
#12 _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:539:8)
#13 PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:137:18)
#14 PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:123:7)
#15 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:439:19)
#16 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:419:22)
#17 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:322:11)
#18 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:374:7)
#19 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:338:5)
#20 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:296:7)
#21 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:279:7)
#25 _invoke1 (dart:ui/hooks.dart:170:10)
#26 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:331:7)
#27 _dispatchPointerDataPacket (dart:ui/hooks.dart:94:31)
(elided 3 frames from dart:async)
Handler: "onTap"
Recognizer: TapGestureRecognizer#ba4ae
debugOwner: GestureDetector
state: possible
won arena
finalPosition: Offset(46.3, 135.7)
finalLocalPosition: Offset(46.3, 26.7)
button: 1
sent tap down

Flutter ScrollController not working under StreamBuilder with ConnectionState condition

I'm new with flutter & just learned how to retrieve data from firestore & did some UI. Right now I have 2 problems;
1- This is a Setting Page which has CustomScrollView contains ScrollController for the controller.
SettingsPage
The controller working fine with this code
class _SettingsPageState extends State<SettingsPage> {
ScrollController _scrollController;
#override
void initState() {
super.initState();
_scrollController = new ScrollController();
_scrollController.addListener(() => setState(() {}));
}
#override
void dispose() {
_scrollController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: Shared.firestore.collection('client').document(Shared.firebaseUser.uid).snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
return Scaffold(
backgroundColor: Colors.grey[50],
body: Stack(
children: <Widget>[
CustomScrollView(
controller: _scrollController,
slivers: <Widget>[
SliverAppBar(
expandedHeight: 140.0,
pinned: true,
),
SliverFillRemaining(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
flex: 8,
child: Column(
children: <Widget>[
Container(
decoration: BoxDecoration(
color: Colors.white,
),
child: Padding(
padding: EdgeInsets.only(
left: 18.0,
top: 18.0,
),
child: Row(
children: <Widget>[
Text(
"Account",
style: TextStyle(
color: Colors.blue[700],
fontSize: 15,
fontWeight: FontWeight.w500,
),
),
],
),
),
),
Container(
decoration: BoxDecoration(
color: Colors.white,
),
child: ListTile(
onTap: () {},
contentPadding: EdgeInsets.only(left: 18.0),
title: Text(
"+${snapshot.data['countryCode']} ${snapshot.data['phoneNumber']}",
style: TextStyle(fontSize: 16),
),
subtitle: Row(
children: <Widget>[
Text(
"Phone Number",
style: TextStyle(
fontSize: 12,
),
),
],
),
),
),
],
),
),
],
),
),
],
),
],
),
);
},
);
}
}
but this error happened
I/flutter ( 8691): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter ( 8691): The following NoSuchMethodError was thrown building StreamBuilder<DocumentSnapshot>(dirty, state:
I/flutter ( 8691): _StreamBuilderBaseState<DocumentSnapshot, AsyncSnapshot<DocumentSnapshot>>#7db43):
I/flutter ( 8691): The method '[]' was called on null.
I/flutter ( 8691): Receiver: null
I/flutter ( 8691): Tried calling: []("countryCode")
I/flutter ( 8691):
I/flutter ( 8691): When the exception was thrown, this was the stack:
I/flutter ( 8691): #0 Object.noSuchMethod (dart:core/runtime/libobject_patch.dart:50:5)
I/flutter ( 8691): #1 _SettingsPageState.build.<anonymous closure> (package:silkwallet/page/settings.dart:81:54)
I/flutter ( 8691): #2 StreamBuilder.build (package:flutter/src/widgets/async.dart:423:74)
I/flutter ( 8691): #3 _StreamBuilderBaseState.build (package:flutter/src/widgets/async.dart:125:48)
I/flutter ( 8691): #4 StatefulElement.build (package:flutter/src/widgets/framework.dart:3809:27)
I/flutter ( 8691): #5 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3721:15)
I/flutter ( 8691): #6 Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
I/flutter ( 8691): #7 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:3701:5)
I/flutter ( 8691): #8 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:3848:11)
I/flutter ( 8691): #9 ComponentElement.mount (package:flutter/src/widgets/framework.dart:3696:5)
I/flutter ( 8691): #10 Element.inflateWidget (package:flutter/src/widgets/framework.dart:2950:14)
I/flutter ( 8691): #11 Element.updateChild (package:flutter/src/widgets/framework.dart:2753:12)
I/flutter ( 8691): #12 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
I/flutter ( 8691): #13 Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
I/flutter ( 8691): #14 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:3701:5)
I/flutter ( 8691): #15 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:3848:11)
I/flutter ( 8691): #16 ComponentElement.mount (package:flutter/src/widgets/framework.dart:3696:5)
I/flutter ( 8691): #17 Element.inflateWidget (package:flutter/src/widgets/framework.dart:2950:14)
I/flutter ( 8691): #18 Element.updateChild (package:flutter/src/widgets/framework.dart:2753:12)
I/flutter ( 8691): #19 SingleChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:4860:14)
I/flutter ( 8691): #20 Element.inflateWidget (package:flutter/src/widgets/framework.dart:2950:14)
I/flutter ( 8691): #21 Element.updateChild (package:flutter/src/widgets/framework.dart:2753:12)
I/flutter ( 8691): #22 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
I/flutter ( 8691): #23 Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
I/flutter ( 8691): #24 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:3701:5)
I/flutter ( 8691): #25 ComponentElement.mount (package:flutter/src/widgets/framework.dart:3696:5)
I/flutter ( 8691): #26 Element.inflateWidget (package:flutter/src/widgets/framework.dart:2950:14)
I/flutter ( 8691): #27 Element.updateChild (package:flutter/src/widgets/framework.dart:2753:12)
.
.
.
I/flutter ( 8691): #135 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:219:5)
I/flutter ( 8691): #136 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:990:15)
I/flutter ( 8691): #137 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:930:9)
I/flutter ( 8691): #138 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:842:5)
I/flutter ( 8691): #139 _invoke (dart:ui/hooks.dart:154:13)
I/flutter ( 8691): #140 _drawFrame (dart:ui/hooks.dart:143:3)
2- After some research for this error, I add connectionState condition inside StreamBuilder which solved above problem
this is the new code, i put the Scaffold inside active connectionState
class _SettingsPageState extends State<SettingsPage> {
ScrollController _scrollController;
#override
void initState() {
super.initState();
_scrollController = new ScrollController();
_scrollController.addListener(() => setState(() {}));
}
#override
void dispose() {
_scrollController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: Shared.firestore.collection('client').document(Shared.firebaseUser.uid).snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
return Scaffold(
backgroundColor: Colors.grey[50],
body: Stack(
.
.
.
),
);
} else if (snapshot.connectionState == ConnectionState.waiting) {
return Container(child: Center(child: CircularProgressIndicator()));
} else {
return Container(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(Icons.warning),
),
Text('Error in loadind data')
],
),
);
}
},
);
}
}
and I realized that when I scrolled the connectionState will change to waiting and the ScrollController not working under this state as shown in the image below
ScrollController: page keep flashing when try to scroll
The reason I want to use the ScrollController because I want to add FloatingActionButton inside Positioned which will scroll along & disappear when reach certain position, here's the image with FloatingActionButton
NoSuchMethodError the method was called on null errors are usually thrown when the method that you're trying to call from the object is yet to be initialized. The error seems to be coming from snapshot.data['countryCode']. What you can do here is add a null-check on snapshot before displaying the data.
if (snapshot != null && snapshot.hasData) {
// Display data
} else {
// Display circular progress...
}

Flutter widget test of a custom widget fails

I am trying to test a custom widget GoogleSignInButton.
Here is the implementation of the widget:
import 'package:flutter/material.dart';
class GoogleSignInButton extends StatelessWidget {
GoogleSignInButton({this.onPressed});
final Function onPressed;
#override
Widget build(BuildContext context) {
Image _buildLogo() {
return Image.asset(
"assets/g-logo.png",
height: 18.0,
width: 18.0,
);
}
Opacity _buildText() {
return Opacity(
opacity: 0.54,
child: Text(
"Sign in with Google",
style: TextStyle(
fontFamily: 'Roboto-Medium',
color: Colors.black,
),
),
);
}
return MaterialButton(
height: 40.0,
onPressed: this.onPressed,
color: Colors.white,
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
_buildLogo(),
SizedBox(width: 24.0),
_buildText(),
],
),
);
}
}
I am trying to test the onPressed function callback by the test that follows.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import '../lib/ui/widgets/google_sign_in_button.dart';
void main() {
testWidgets('my first widget test', (WidgetTester tester) async {
var pressed = false;
var widget = GoogleSignInButton(
onPressed: () => () {
pressed = true;
},
);
await tester.pumpWidget(
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return MaterialApp(
home: Material(
child: Center(
child: widget,
),
),
);
},
),
);
await tester.press(find.byWidget(widget));
expect(pressed, equals(true));
});
}
Unfortunately, the test fails.
I am executing my widget test on the command line by flutter test test/widget_test.dart and here is the result of the test:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure object was thrown running a test:
Expected: <true>
Actual: <false>
When the exception was thrown, this was the stack:
#4 main.<anonymous closure> (file:///home/hans/Development/flutter/recipes_app/test/widget_test.dart:30:5)
<asynchronous suspension>
#5 testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:72:23)
#6 TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:566:19)
<asynchronous suspension>
#9 TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:550:14)
#10 AutomatedTestWidgetsFlutterBinding.runTest.<anonymous closure> (package:flutter_test/src/binding.dart:893:24)
#16 AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:890:15)
#17 testWidgets.<anonymous closure> (package:flutter_test/src/widget_tester.dart:71:22)
#18 Declarer.test.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:168:27)
<asynchronous suspension>
#19 Invoker.waitForOutstandingCallbacks.<anonymous closure> (package:test_api/src/backend/invoker.dart:249:15)
<asynchronous suspension>
#24 Invoker.waitForOutstandingCallbacks (package:test_api/src/backend/invoker.dart:246:5)
#25 Declarer.test.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:166:33)
#30 Declarer.test.<anonymous closure> (package:test_api/src/backend/declarer.dart:165:13)
<asynchronous suspension>
#31 Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart:399:25)
<asynchronous suspension>
#45 _Timer._runTimers (dart:isolate/runtime/libtimer_impl.dart:382:19)
#46 _Timer._handleMessage (dart:isolate/runtime/libtimer_impl.dart:416:5)
#47 _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:171:12)
(elided 28 frames from class _FakeAsync, package dart:async, and package stack_trace)
This was caught by the test expectation on the following line:
file:///home/hans/Development/flutter/recipes_app/test/widget_test.dart line 30
The test description was:
my first widget test
════════════════════════════════════════════════════════════════════════════════════════════════════
00:01 +0 -1: my first widget test [E]
Test failed. See exception logs above.
The test description was: my first widget test
00:02 +0 -1: Some tests failed.
Any ideas why the test fails?
Your closure is wrong:
() => () {
pressed = true;
},
You need to write it either like that
() {
pressed = true;
},
Or like that
() => ( pressed = true )
And you need to trigger the tester.tap method.
That's my working code:
var pressed = false;
var widget = GoogleSignInButton(
onPressed: () {
pressed = true;
debugPrint("Pressed!");
},
);
await tester.pumpWidget(
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return MaterialApp(
home: Material(
child: Center(
child: widget,
),
),
);
},
),
);
expect(find.byWidget(widget), findsOneWidget);
await tester.tap(find.byWidget(widget));
expect(pressed, isTrue);
And I've supposed that when you run your project your Widget renders well and does not throw any errors.
HINT: make a flutter clean after any change in your test_file.dart change.

How to properly set value of DropdownButton using Bloc in Flutter?

I'm new in Bloc programming pattern and I'm having an issue when using it in with Dropdown
That's in my bloc class:
final _dropDown = BehaviorSubject<String>();
Stream<String> get dropDownStream => _dropDown.stream;
Sink<String> get dropDownSink => _dropDown.sink;
final _dropdownValues = BehaviorSubject<List<String>>(seedValue: [
'One',
'Two',
'Three',
'Four',
].toList());
Stream<List<String>> get dropdownValuesStream => _dropdownValues.stream;
In my widget page I added the following dropdown widget so that everything is handled by the Bloc class:
StreamBuilder<List<String>>(
stream: _exampleBloc.dropdownValuesStream,
builder: (BuildContext contextValues, AsyncSnapshot snapshotValues) {
return StreamBuilder<String>(
stream: _exampleBloc.dropDownStream,
builder: (BuildContext context, AsyncSnapshot snapshot) {
return InputDecorator(
decoration: InputDecoration(
icon: const Icon(Icons.color_lens),
labelText: 'DropDown',
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: snapshot.data,
onChanged: (String newValue) => _exampleBloc.dropDownSink.add(newValue),
items: snapshotValues.data != null ? snapshotValues.data.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList() : <String>[''].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
),
);
},
);
},
),
But doing like that, I get this error when setting the value (value: snapshot.data) of the DropdownButton:
I/flutter ( 5565): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter ( 5565): The following assertion was thrown building StreamBuilder<String>(dirty, state:
I/flutter ( 5565): _StreamBuilderBaseState<String, AsyncSnapshot<String>>#70482):
I/flutter ( 5565): 'package:flutter/src/material/dropdown.dart': Failed assertion: line 514 pos 15: 'items == null ||
I/flutter ( 5565): value == null || items.where((DropdownMenuItem<T> item) => item.value == value).length == 1': is not
I/flutter ( 5565): true.
I/flutter ( 5565):
I/flutter ( 5565): Either the assertion indicates an error in the framework itself, or we should provide substantially
I/flutter ( 5565): more information in this error message to help you determine and fix the underlying cause.
I/flutter ( 5565): In either case, please report this assertion by filing a bug on GitHub:
I/flutter ( 5565): https://github.com/flutter/flutter/issues/new?template=BUG.md
I/flutter ( 5565):
I/flutter ( 5565): When the exception was thrown, this was the stack:
I/flutter ( 5565): #2 new DropdownButton (package:flutter/src/material/dropdown.dart:514:15)
I/flutter ( 5565): #3 _ExamplePageState.build.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:financeiro_mobile/src/ui/exemple/example_page.dart:129:42)
I/flutter ( 5565): #4 StreamBuilder.build (package:flutter/src/widgets/async.dart:423:74)
I/flutter ( 5565): #5 _StreamBuilderBaseState.build (package:flutter/src/widgets/async.dart:125:48)
I/flutter ( 5565): #6 StatefulElement.build (package:flutter/src/widgets/framework.dart:3809:27)
I/flutter ( 5565): #7 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3721:15)
I/flutter ( 5565): #8 Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
I/flutter ( 5565): #9 StatefulElement.update (package:flutter/src/widgets/framework.dart:3878:5)
I/flutter ( 5565): #10 Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
I/flutter ( 5565): #11 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
I/flutter ( 5565): #12 Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
I/flutter ( 5565): #13 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2286:33)
I/flutter ( 5565): #14 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding&WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:676:20)
I/flutter ( 5565): #15 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:219:5)
I/flutter ( 5565): #16 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:990:15)
I/flutter ( 5565): #17 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:930:9)
I/flutter ( 5565): #18 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:842:5)
I/flutter ( 5565): #19 _invoke (dart:ui/hooks.dart:154:13)
I/flutter ( 5565): #20 _drawFrame (dart:ui/hooks.dart:143:3)
I/flutter ( 5565): (elided 2 frames from class _AssertionError)
I tried a lot of ideas like checking if snapshotValues.data is not null when setting. I know that the value has to be something from the list or null. But no logic that I put there makes this error go away.
If I set the value to null, it works, but then the selected value doesn't show.
Am I doing this wrong? Is there a better way that works? How can I solve this issue?
Thanks!
I solved it using two stream in the bloc, one for the list of elemtns and the other for the value. So in ther build, you need two chained StreamBuilders and when u got both snapshots with data, you load the build. Like that:
Widget _holdingDropDown() {
return StreamBuilder(
stream: bloc.holding,
builder: (BuildContext context, AsyncSnapshot<Holding> snapshot) {
return Container(
child: Center(
child: snapshot.hasData
? StreamBuilder(
stream: bloc.obsHoldingList,
builder: (BuildContext context,
AsyncSnapshot<List<Holding>> holdingListSnapshot) {
return holdingListSnapshot.hasData ?
DropdownButton<Holding>(
value: snapshot.data,
items: _listDropDownHoldings,
onChanged: (Holding h) {
_changeDropDownItemHolding(h);
},
): CircularProgressIndicator();
},
)
: CircularProgressIndicator(),
),
);
});
}
I use the circular progress indicator to return if I don't have the snapshot with data.
I hope I have been helpful.
That's because you are using a StreamBuilder, so at the first time your snapshot is empty, you have to do a validation :
return snapshot.hasData ?
InputDecorator(
decoration: InputDecoration(
icon: const Icon(Icons.color_lens),
labelText: 'DropDown',
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: snapshot.data,
onChanged: (String newValue) => _exampleBloc.dropDownSink.add(newValue),
items: snapshotValues.data != null ? snapshotValues.data.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList() : SizedBox(height: 0.0)
),
) : SizedBox(height: 0.0);
Display an empty widget SizedBox(height: 0.0) or a CircleProgressIndicator
I use just Bloc for setting and changing Dropdown values, without using stream (in example I use two dropdowns with different values). Below part of real script from my app:
class LangsInput extends StatelessWidget {
const LangsInput({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return BlocBuilder<WordGroupFormBloc, WordGroupFormState>(
buildWhen: (previous, current) => previous != current,
builder: (context, state) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
DropdownButton<String>(
value: state.langStudy.value,
icon: const Icon(Icons.arrow_drop_down),
elevation: 16,
onChanged: (String? newValue) {
context.read<WordGroupFormBloc>().add(LangStudyChanged(langStudy: newValue as String));
},
items: getLanguageList(),
),
DropdownButton<String>(
value: state.langTransl.value,
icon: const Icon(Icons.arrow_drop_down),
elevation: 16,
onChanged: (String? newValue) {
context.read<WordGroupFormBloc>().add(LangTranslChanged(langTransl: newValue as String));
},
items: getLanguageList(),
),
]
);
}
);
}
}
and part of script example from bloc:
void _onLangStudyChanged(LangStudyChanged event, Emitter<WordGroupFormState> emit) {
final langStudy = LangStudy.dirty(event.langStudy);
emit(state.copyWith(
langStudy: langStudy.valid ? langStudy : LangStudy.pure(event.langStudy),
status: Formz.validate([langStudy]),
));
}

In Flutter iconButton is click in appBar but error is coming

i have been trying to trigger a dialog-box when the iconButton is click in appBar but error is coming
this error is persistance.
I think that the context passed in the showDialog() have some issue i'm not sure
import 'package:flutter/material.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
## Heading ##
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return _MyPortfolioState();
}
}
class _MyPortfolioState extends State<MyApp> {
MaterialColor primaryColor = Colors.blue;
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Portfolio',
theme: new ThemeData(
primarySwatch: primaryColor,
),
home: Scaffold(
appBar: AppBar(
title: Text("Dhruv Agarwal"),
textTheme: TextTheme(
display2: TextStyle(color: Color.fromARGB(1, 0, 0, 0))),
actions: <Widget>[
new IconButton(
icon: Icon(Icons.format_color_text),
tooltip: 'Change Color',
onPressed: () {
Color pickerColor = primaryColor;
changeColor(Color color) {
setState(() => primaryColor = color);
}
showDialog (
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Pick a color!'),
content: SingleChildScrollView(
child: ColorPicker(
pickerColor: pickerColor,
onColorChanged: changeColor,
pickerAreaHeightPercent: 0.8,
),
),
actions: <Widget>[
FlatButton(
child: Text('Got it'),
onPressed: () {
setState(() => primaryColor = pickerColor);
Navigator.of(context).pop();
},
),
],
);
});
},
),
])));
}
}
//
//
The exception is thrown on reload that No MaterialLocalizations found.
Launching lib/main.dart on Redmi Note 5 Pro in debug mode...
Initializing gradle...
Resolving dependencies...
Gradle task 'assembleDebug'...
Built build/app/outputs/apk/debug/app-debug.apk.
Installing build/app/outputs/apk/app.apk...
I/zygote64(16669): Do partial code cache collection, code=28KB, data=20KB
I/zygote64(16669): After code cache collection, code=28KB, data=20KB
I/zygote64(16669): Increasing code cache capacity to 128KB
Syncing files to device Redmi Note 5 Pro...
I/flutter (16669): ══╡ EXCEPTION CAUGHT BY GESTURE ╞═══════════════════════════════════════════════════════════════════
I/flutter (16669): The following assertion was thrown while handling a gesture:
I/flutter (16669): No MaterialLocalizations found.
I/flutter (16669): MyApp widgets require MaterialLocalizations to be provided by a Localizations widget ancestor.
I/flutter (16669): Localizations are used to generate many different messages, labels,and abbreviations which are used
I/flutter (16669): by the material library.
I/flutter (16669): To introduce a MaterialLocalizations, either use a MaterialApp at the root of your application to
I/flutter (16669): include them automatically, or add a Localization widget with a MaterialLocalizations delegate.
I/flutter (16669): The specific widget that could not find a MaterialLocalizations ancestor was:
I/flutter (16669): MyApp
I/flutter (16669): The ancestors of this widget were:
I/flutter (16669): [root]
I/flutter (16669):
I/flutter (16669): When the exception was thrown, this was the stack:
I/flutter (16669): #0 debugCheckHasMaterialLocalizations.<anonymous closure> (package:flutter/src/material/debug.dart:124:7)
I/flutter (16669): #1 debugCheckHasMaterialLocalizations (package:flutter/src/material/debug.dart:127:4)
I/flutter (16669): #2 showDialog (package:flutter/src/material/dialog.dart:606:10)
I/flutter (16669): #3 MyApp.build.<anonymous closure> (file:///home/punisher/AndroidStudioProjects/first_app/portfolio/lib/main.dart:32:19)
I/flutter (16669): #4 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:507:14)
I/flutter (16669): #5 _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:562:30)
I/flutter (16669): #6 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:102:24)
I/flutter (16669): #7 TapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:242:9)
I/flutter (16669): #8 TapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:204:7)
I/flutter (16669): #9 GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:156:27)
I/flutter (16669): #10 _WidgetsFlutterBinding&BindingBase&GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:147:20)
I/flutter (16669): #11 _WidgetsFlutterBinding&BindingBase&GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:121:22)
I/flutter (16669): #12 _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:101:7)
I/flutter (16669): #13 _WidgetsFlutterBinding&BindingBase&GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:64:7)
I/flutter (16669): #14 _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:48:7)
I/flutter (16669): #15 _invoke1 (dart:ui/hooks.dart:153:13)
I/flutter (16669): #16 _dispatchPointerDataPacket (dart:ui/hooks.dart:107:5)
I/flutter (16669):
I/flutter (16669): Handler: onTap
I/flutter (16669): Recognizer:
I/flutter (16669): TapGestureRecognizer#5070d(debugOwner: GestureDetector, state: ready, won arena, finalPosition:
I/flutter (16669): Offset(354.2, 64.0), sent tap down)
I/flutter (16669): ════════════════════════════════════════════════════════════════════════════════════════════════════
I/flutter (16669): Another exception was thrown: No MaterialLocalizations found.
Some Material Widget in your tree requires localization to be set, so instead of running directly your app from a StatefulWidget, wrap it in a MaterialApp. So the MaterialApp will have your Widget as its child, and not the contrary.
main() => runApp(
MaterialApp(
title: 'ColorPicker test',
home: MyApp(),
),
);
The MaterialApp will setup the MaterialLocalizations in the widget tree and its children will be able to use it.
By the way, your code won't work as the ColorPicker chooses from all possible colors and the primarySwatch can only be MaterialColors. You can try again using a material ColorPicker. I think this plugin also provides it, you can check this one too.

Resources