How to initialized and dispose camera controller properly. I am using camera: ^0.9.4+5
Along with many
other errors
I am getting this error on switch camera button click:
======== Exception caught by widgets library =======================================================
The following CameraException was thrown building ValueListenableBuilder<CameraValue>(dirty, state: _ValueListenableBuilderState<CameraValue>#47f17):
CameraException(Disposed CameraController, buildPreview() was called on a disposed CameraController.)
Here is the complete code:
class VideoRecorderScreen extends StatefulWidget {
#override
_VideoRecorderScreenState createState() {
return _VideoRecorderScreenState();
}
//final ApplicantData applicantData;
final String videorec;
VideoRecorderScreen(this.applicationId, this.name, this.description,
this.descriptionHindi, this.videorec,
{this.isOpenSubsDashboard = false});
}
class _VideoRecorderScreenState extends State<VideoRecorderScreen> {
CameraController controller;
XFile videoPath;
List<CameraDescription> cameras;
int selectedCameraIdx = 1;
int videoElapseTime = 25;
int videoMinElapseTime = 20;
bool visibilityStop = false;
bool visibilityButtons = false;
String timer = "00:00";
CountdownTimer cd;
bool startRecording = false;
void _reset() {
videoPath = null;
_changed(false);
_changedTimer("00:00");
_changedButtonVisibility(false);
}
void _changed(bool visibility) {
if (mounted) setState(() {
visibilityStop = visibility;
});
}
void _changedTimer(String time) {
if (mounted) setState(() {
timer = time;
});
}
void _changedButtonVisibility(bool visibility) {
if (mounted) setState(() {
visibilityButtons = visibility;
});
}
void _navigateAndDisplaySelection(BuildContext context) async {
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => VideoPlayerScreen(
videoPath: videoPath.path,
applicationId: widget.applicationId,
name: widget.name,
dateTime: selectedDate,
isOpenSubsDashboard: widget.isOpenSubsDashboard)),
);
if (result) {
_reset();
}
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) =>
showSnackBar('User Validated Successfully! Welcome ' + widget.name));
availableCameras().then((availableCameras) {
cameras = availableCameras;
if (cameras.length > 0) {
for (int i = 0; i < cameras.length; i++) {
if (cameras[i].lensDirection == CameraLensDirection.front) {
if (mounted) setState(() {
selectedCameraIdx = i;
});
}
}
_onCameraSwitched(cameras[selectedCameraIdx]).then((void v) {});
}
}).catchError((err) {
print('Error: $err.code\nError Message: $err.message');
});
}
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size.width * (2 / 3);
return WillPopScope(
onWillPop: _onBackPressed,
child: Scaffold(
key: _scaffoldKey,
appBar: AppBarWidget(
title: 'Record Your Video',
actions: [
IconButton(
icon: Icon(
Icons.call,
),
onPressed: () {
ConstantMethods.callToSupport();
}),
AppBarLogo(),
],
),
body: Builder(builder: (BuildContext buildContext) {
scaffold = Scaffold.of(buildContext);
return Container(
child: ListView(
children: <Widget>[
Center(
child: Container(
width: size / 1.5,
height: size / 1.5,
Container(
padding: EdgeInsets.all(5.0),
color: Colors.grey[850],
child: Text(
selectedDate != null ? selectedDate : "",
textAlign: TextAlign.start,
),
],
),
))
: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
VerticalSpacing(20.0),
Padding(
padding:
const EdgeInsets.only(left: 8.0, right: 8.0),
child: Text(
'Click on video icon to start the recording',
textAlign: TextAlign.justify,
FlatButton(
child: Text(' Proceed for Recording '),
onPressed: () {
if (mounted) setState(() {
startRecording = true;
});
},
),
VerticalSpacing(10.0),
FlatButton(
textColor: ThemeColors.colorPrimary,
VerticalSpacing(20.0),
],
),
startRecording
? Padding(
padding: const EdgeInsets.all(5.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
_cameraTogglesRowWidget(),
_captureControlRowWidget(),
Expanded(
child: Text("$timer",
textAlign: TextAlign.end,
style: TextStyle(
fontSize: Consts.fontSizeMedium)),
),
],
),
)
: new Container(),
!visibilityButtons
? Column(
children: [
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,
//alignment: Alignment.bottomRight,
children: [
IconButton(
icon: const Icon(Icons.info_outline,
size: 25.0),
color: ThemeColors.colorPrimary,
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) =>
MessageDialog(
title: "ImportantTips",
message: Languages.of(context)
.videoPreInstruction,
),
);
},
),
ToggleButtons(
borderColor: ThemeColors.colorAccent,
fillColor: Colors.lightBlue[100],
borderWidth: 1,
selectedBorderColor: ThemeColors.colorAccent,
selectedColor: Colors.blue[900],
color: ThemeColors.colorPrimary,
borderRadius: BorderRadius.circular(10),
children: <Widget>[
Padding(
padding: const EdgeInsets.only(
left: 12.0, right: 8.0),
child: Text(
'In English',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: Consts.fontSizeSmall),
),
),
Padding(
padding: const EdgeInsets.only(
left: 8.0, right: 8.0),
child: Text(
'हिंदी में ',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: Consts.fontSizeSmall),
),
),
],
onPressed: (int index) {
if(this.mounted) setState(() {
for (int i = 0;
i < isSelectedLanguage.length;
i++) {
if (i == index) {
isSelectedLanguage[i] = true;
} else {
isSelectedLanguage[i] = false;
}
}
if (isSelectedLanguage[0]) {
changeLanguage(context, 'en');
} else if (isSelectedLanguage[1]) {
changeLanguage(context, 'hi');
}
});
},
isSelected: isSelectedLanguage,
)
]),
VerticalSpacing(10.0),
Padding(
padding:
const EdgeInsets.only(left: 8.0, right: 8.0),
child
: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
OutlineButton(
padding: EdgeInsets.all(5.0),
splashColor: Colors.blue[300],
borderSide:
BorderSide(color: ThemeColors.colorPrimary),
child: Text(' Retake Video ',
style: TextStyle(
color: Colors.blue,
fontSize: Consts.fontSizeMedium)),
onPressed: () {
_reset();
},
),
HorizontalSpacing(10.0),
FlatButton(
padding: EdgeInsets.all(5.0),
color: ThemeColors.colorPrimary,
// splashColor: Colors.greenAccent,
textColor: ThemeColors.white,
child: Text(' Play Recording ',
style:
TextStyle(fontSize: Consts.fontSizeMedium)),
onPressed: () {
if(videoPath!=null) {
_navigateAndDisplaySelection(context);
} else{
//ConstantMethods.showToast("Technical exception with camera.");
ConstantMethods.showToast("There is some issue in recording video right now, please try again later.");
_onBackPressed();
}
},
),
],
),
VerticalSpacing(10.0),
],
));
})),
);
}
IconData _getCameraLensIcon(CameraLensDirection direction) {
switch (direction) {
case CameraLensDirection.back:
return Icons.camera_rear;
case CameraLensDirection.front:
return Icons.camera_front;
case CameraLensDirection.external:
return Icons.camera;
default:
return Icons.device_unknown;
}
}
// Display 'Loading' text when the camera is still loading.
Widget _cameraPreviewWidget(var size) {
if (controller == null || !controller.value.isInitialized) {
return const Text(
'Loading',
style: TextStyle(
color: ThemeColors.white,
fontSize: 20.0,
fontWeight: FontWeight.w900,
),
);
}
return Container(
width: size,
height: size,
child: ClipRect(
child: OverflowBox(
alignment: Alignment.center,
child: FittedBox(
fit: BoxFit.fitWidth,
child: Container(
width: size,
height: size / controller.value.aspectRatio,
// height: size / controller.value.aspectRatio,
child: CameraPreview(controller),
// this is my CameraPreview
),
),
),
),
);
}
/// Display a row of toggle to select the camera (or a message if no camera is available).
Widget _cameraTogglesRowWidget() {
if (cameras == null) {
return Row();
}
CameraDescription selectedCamera =
cameras[selectedCameraIdx];
CameraLensDirection lensDirection =
selectedCamera.lensDirection;
//CameraLensDirection lensDirection = CameraLensDirection.front;
return Expanded(
child: Align(
alignment: Alignment.centerLeft,
child: FlatButton.icon(
onPressed: controller != null && controller.value.isRecordingVideo
? null
: _onSwitchCamera,
icon: Icon(_getCameraLensIcon(lensDirection)),
label: Text(
"${lensDirection.toString().substring(lensDirection.toString().indexOf('.') + 1)}")),
),
);
}
/// Display the control bar with buttons to record videos.
Widget _captureControlRowWidget() {
return Expanded(
child: Align(
alignment: Alignment.center,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
IconButton(
icon: const Icon(Icons.videocam, size: 40.0),
color: ThemeColors.colorPrimary,
onPressed: controller != null &&
controller.value.isInitialized &&
!controller.value.isRecordingVideo &&
videoPath == null
? _onRecordButtonPressed
: null,
),
IconButton(
icon: const Icon(Icons.stop, size: 40.0),
color: ThemeColors.red,
onPressed: controller != null &&
controller.value.isInitialized &&
controller.value.isRecordingVideo &&
visibilityStop
? _onStopButtonPressed
: null,
),
// : new Container(),
],
),
),
);
}
String timestamp() => DateTime.now().millisecondsSinceEpoch.toString();
Future<void> _onCameraSwitched(CameraDescription description) async {
if (description == null) {
return;
}
if(!(controller != null && controller.value.isRecordingVideo)){
onNewCameraSelected(description);
}
}
void onNewCameraSelected(CameraDescription cameraDescription) async {
if (controller != null) {
await controller.dispose();
}
controller = CameraController(
cameraDescription, ResolutionPreset.low
);
//controller = cameraController;
// If the controller is updated then update the UI.
controller.addListener(() {
if (mounted) setState(() {});
if (controller.value.hasError) {
Fluttertoast.showToast(
msg: 'Camera error ${controller.value.errorDescription}',
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: ThemeColors.red,
textColor: ThemeColors.white);
}
});
try {
await controller.initialize();
} on CameraException catch (e) {
_showCameraException(e);
}
if (mounted) {
setState(() {});
}
}
void _onSwitchCamera() {
selectedCameraIdx =
selectedCameraIdx < cameras.length - 1 ? selectedCameraIdx + 1 : 0;
CameraDescription selectedCamera = cameras[selectedCameraIdx];
_onCameraSwitched(selectedCamera);
if (mounted) setState(() {
selectedCameraIdx = selectedCameraIdx;
});
}
void _onRecordButtonPressed() {
_startVideoRecording().then((_) {
/*if (mounted) setState(() {});*/
//if (filePath != null) {
final df = new DateFormat('dd-MM-yyyy hh:mm a');
String date = df.format(new DateTime.now());
if (mounted) setState(() {
// selectedDate = date1.replaceAll("/", "-");
selectedDate = date;
});
Fluttertoast.showToast(
msg: 'Recording video started',
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: ThemeColors.grey,
textColor: ThemeColors.white);
// }
});
}
void _onStopButtonPressed() {
if (cd.isRunning) {
cd.cancel();
}
_stopVideoRecording().then((file) {
if (mounted) setState(() {});
if (file != null) {
showInSnackBar('Video recorded to ${file.path}');
videoPath = file;
}
});
}
Future<void> _startVideoRecording() async {
//final CameraController? cameraController = controller;
if (controller == null || !controller.value.isInitialized) {
//showInSnackBar('Error: select a camera first.');
Fluttertoast.showToast(
msg: 'Please wait',
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: ThemeColors.grey,
textColor: ThemeColors.white);
return;
}
if (controller.value.isRecordingVideo) {
// A recording is already started, do nothing.
return;
}
try {
cd = CountdownTimer(
new Duration(seconds: videoElapseTime), new Duration(seconds: 1));
// var sub = cd.listen(null);
var sub = cd.listen(null);
sub.onData((duration) {
// setState(() { _current = _start - duration.elapsed.inSeconds; });
if (duration.elapsed.inSeconds == videoMinElapseTime) {
// ((videoElapseTime - 5) < 20 ? 20 : (videoElapseTime - 5))) {
_changed(true);
}
timer = duration.elapsed.inMinutes.toString().padLeft(2, '0') +
":" +
duration.elapsed.inSeconds.toString().padLeft(2, '0');
_changedTimer(timer + "/00:" + videoElapseTime.toString());
print(timer);
});
sub.onDone(() {
print("Done");
_onStopButtonPressed();
sub.cancel();
});
await controller.startVideoRecording();
} on CameraException catch (e) {
_showCameraException(e);
return;
}
}
Future<XFile> _stopVideoRecording() async {
if (controller == null || !controller.value.isRecordingVideo) {
return null;
}
try {
_changedButtonVisibility(true);
XFile video = await controller.stopVideoRecording();
await GallerySaver.saveVideo(video.path); //for testing
return video;
} on CameraException catch (e) {
_showCameraException(e);
return null;
}
}
void _showCameraException(CameraException e) {
String errorText = 'Error: ${e.code}\nError Message: ${e.description}';
print(errorText);
}
Future<bool> _onBackPressed() {
if (widget.isOpenSubsDashboard && (role == null || role.isEmpty)) {
//subscriber
return Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => SubscriberDashboard()),
(route) => false,
) ??
false;
} else {
//Navigator.of(context).pop();
return Future.value(Navigator.of(context).canPop());
}
}
One way of resolving this issue is by using a WidgetsBindingObserver - this helps manages the app's lifecycle. Inherit the WidgetsBidingObserver in your _VideoRecorderScreenState class to dispose the camera properly. A similar approach is demonstrated on this GitHub issue thread.
Related
I am working on a large-scale project and integrating video calls into it. So I made a separate project just for practice and I achieved good results. The group calling worked perfectly on both android and IOS. But then I integrated the same code in my large-scale project which uses firebase as a backend and when I navigate to the video screen it gives me an error saying "Unhandled Exception: LateInitializationError: Field 'requestPort' has not been initialized". The Agora console is on Testing for now and the channel wasn't expired just in case you guys are wondering. As I said it works perfectly in a separate project.
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:flutter/material.dart';
import 'package:logger/logger.dart';
class VideoCallPage extends StatefulWidget {
const VideoCallPage({Key? key}) : super(key: key);
#override
VideoCallPageState createState() => VideoCallPageState();
}
class VideoCallPageState extends State<VideoCallPage> {
static final _users = <int>[];
Logger logger = Logger();
bool muted = false;
late RtcEngine _engine;
bool isRigning = true;
bool isSpeakerOn = true;
final String channelName = 'video';
final String appID = 'xxx';
final String tokenAudio ='xxx';
#override
void dispose() {
_dispose();
super.dispose();
}
Future<void> _dispose() async {
_users.clear();
await _engine.leaveChannel();
await _engine.stopPreview();
await _engine.release();
}
#override
void initState() {
super.initState();
// initialize agora sdk
initialize();
}
Future<void> initialize() async {
logger.i('Initialize');
if (appID.isEmpty) {
setState(() {
logger.e(
'APP_ID missing, please provide your APP_ID in settings.dart',
);
logger.e('Agora Engine is not starting');
});
return;
}
await _initAgoraRtcEngine();
_addAgoraEventHandlers();
onOffSpeaker();
await _engine.joinChannel(
token: tokenAudio,
channelId: channelName,
uid: 0,
options: const ChannelMediaOptions(
channelProfile: ChannelProfileType.channelProfileCommunication,
clientRoleType: ClientRoleType.clientRoleBroadcaster));
}
Future<void> _initAgoraRtcEngine() async {
logger.i('_initAgoraRtcEngine');
//create the engine
_engine = createAgoraRtcEngine();
logger.i('RtcEngineContext');
await _engine.initialize(RtcEngineContext(
appId: appID,
));
logger.i('enablbing video');
await _engine.enableVideo();
// await _engine.setVideoEncoderConfiguration(
// const VideoEncoderConfiguration(
// dimensions: VideoDimensions(width: 640, height: 360),
// frameRate: 15,
// bitrate: 0,
// ),
// );
await _engine.startPreview();
}
void _addAgoraEventHandlers() {
_engine.registerEventHandler(RtcEngineEventHandler(
onError: (ErrorCodeType errorCodeType, String value) {
if (mounted) {
setState(() {
final info = 'onError: ${errorCodeType.name}';
logger.e(info);
});
}
},
onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
setState(() {
final info =
'onJoinChannel: ${connection.channelId}, uid: ${connection.localUid}';
logger.i(info);
});
},
onLeaveChannel: (RtcConnection rtcConnection, RtcStats rtcStats) {
setState(() {
logger.i('onLeaveChannel');
_users.clear();
});
},
onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) {
setState(() {
isRigning = false;
final info = 'remoteUserJoined: $remoteUid';
logger.i(info);
_users.add(remoteUid);
});
},
onUserOffline: (RtcConnection connection, int remoteUid,
UserOfflineReasonType reason) {
setState(() {
final info =
'remoteUserOffline: $remoteUid , reason: ${reason.index}';
logger.i(info);
_users.remove(remoteUid);
});
},
onFirstRemoteVideoFrame: (connection, uid, width, height, elapsed) {
setState(() {
final info = 'firstRemoteVideoFrame: $uid';
logger.i(info);
});
},
));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Agora Group Video Calling'),
),
backgroundColor: Colors.black,
body: Center(
child: Stack(
children: <Widget>[
Opacity(
opacity: isRigning ? 0.2 : 1,
child: _viewRows(),
),
_toolbar(),
if (isRigning)
Positioned(
top: 100,
left: MediaQuery.of(context).size.width * 0.3,
right: MediaQuery.of(context).size.width * 0.3,
child: Center(
child: Text(
'Ringing...',
style: TextStyle(color: Colors.white, fontSize: 30),
),
))
],
),
),
);
}
/// Helper function to get list of native views
List<Widget> _getRenderViews() {
final List<StatefulWidget> list = [];
list.add(AgoraVideoView(
controller: VideoViewController(
rtcEngine: _engine,
canvas: const VideoCanvas(uid: 0),
)));
_users.forEach((int uid) => list.add(AgoraVideoView(
controller: VideoViewController.remote(
rtcEngine: _engine,
canvas: VideoCanvas(uid: uid),
connection: RtcConnection(channelId: channelName),
),
)));
return list;
}
/// Video view wrapper
Widget _videoView(view) {
return Expanded(child: Container(child: view));
}
/// Video view row wrapper
Widget _expandedVideoRow(List<Widget> views) {
final wrappedViews = views.map<Widget>(_videoView).toList();
return Expanded(
child: Row(
children: wrappedViews,
),
);
}
Widget _viewRows() {
final views = _getRenderViews();
switch (views.length) {
case 1:
return Container(
child: Column(
children: <Widget>[_videoView(views[0])],
));
case 2:
return Container(
child: Column(
children: <Widget>[
_expandedVideoRow([views[0]]),
_expandedVideoRow([views[1]])
],
));
case 3:
return Container(
child: Column(
children: <Widget>[
_expandedVideoRow(views.sublist(0, 2)),
_expandedVideoRow(views.sublist(2, 3))
],
));
case 4:
return Container(
child: Column(
children: <Widget>[
_expandedVideoRow(views.sublist(0, 2)),
_expandedVideoRow(views.sublist(2, 4))
],
));
default:
}
return Container();
}
Widget _toolbar() {
return Container(
alignment: Alignment.bottomCenter,
padding: const EdgeInsets.symmetric(vertical: 48),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RawMaterialButton(
onPressed: onOffSpeaker,
child: Icon(
isSpeakerOn ? Icons.volume_up_sharp : Icons.volume_off,
color: isSpeakerOn ? Colors.white : Colors.blueAccent,
size: 20.0,
),
shape: CircleBorder(),
elevation: 2.0,
fillColor: isSpeakerOn ? Colors.blueAccent : Colors.white,
padding: const EdgeInsets.all(12.0),
),
RawMaterialButton(
onPressed: _onToggleMute,
child: Icon(
muted ? Icons.mic_off : Icons.mic,
color: muted ? Colors.white : Colors.blueAccent,
size: 20.0,
),
shape: CircleBorder(),
elevation: 2.0,
fillColor: muted ? Colors.blueAccent : Colors.white,
padding: const EdgeInsets.all(12.0),
),
RawMaterialButton(
onPressed: () => _onCallEnd(context),
child: Icon(
Icons.call_end,
color: Colors.white,
size: 35.0,
),
shape: CircleBorder(),
elevation: 2.0,
fillColor: Colors.redAccent,
padding: const EdgeInsets.all(15.0),
),
RawMaterialButton(
onPressed: _onSwitchCamera,
child: Icon(
Icons.switch_camera,
color: Colors.blueAccent,
size: 20.0,
),
shape: CircleBorder(),
elevation: 2.0,
fillColor: Colors.white,
padding: const EdgeInsets.all(12.0),
)
],
),
);
}
void _onToggleMute() {
setState(() {
muted = !muted;
});
_engine.muteLocalAudioStream(muted);
}
void _onCallEnd(BuildContext context) {
Navigator.pop(context);
}
void _onSwitchCamera() {
_engine.switchCamera();
}
Future onOffSpeaker() async {
setState(() {
isSpeakerOn = !isSpeakerOn;
});
await _engine.setEnableSpeakerphone(isSpeakerOn);
}
}
I used Logger package to view logs and I found out the error occurs in this piece of code
await _engine.initialize(RtcEngineContext(
appId: appID,
));
The app uses agora_rtc_engine: ^6.1.0 and defined in pubspec.yaml file
In Agora SDK, there must be a late field requestPort which is accessed before the initialization.
It seems already similar issue raised in Agora SDK github repo. But its closed.
You can open a new issue or reopen existing with steps to reproduce the issue.
I am trying to get a barcode to scan an item and retrieve the item from cloud firestore database. The application is meant to then display the item onto the homepage.
But i keep having the same error.
I have tried to change it to Map<Dynamic, Dynamic>. I have also tried to remove it from not being null Map<String, dynamic> no question mark. All flutter lib are up to date as well as the pubspec.
The code below is for my homepage
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/services.dart';
import 'package:flutter_barcode_scanner/flutter_barcode_scanner.dart';
Map<String, dynamic>? mapProducts = {};
//Initialize
#override
void initState() {
if (userProfile.containsKey("Unordered Products")) {
mapProducts = userProfile["Unordered Products"];
}
super.initState();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
double size =
MediaQuery.of(context).size.width < MediaQuery.of(context).size.height
? MediaQuery.of(context).size.width
: (!kIsWeb)
? MediaQuery.of(context).size.height
: MediaQuery.of(context).size.height / 2;
//For Refreshing the theme
if (userProfile.containsKey("Theme")) {
myAppTheme = userProfile["Theme"] == "Light Theme"
? getMainThemeWithBrightness(context, Brightness.light)
: getMainThemeWithBrightness(context, Brightness.dark);
}
MobileScannerController cameraController = MobileScannerController();
return SafeArea(
child: Scaffold(
key: scaffoldKey,
backgroundColor: myAppTheme?.scaffoldBackgroundColor,
appBar: getAppBar(
scaffoldKey: scaffoldKey,
context: context,
strAppBarTitle: "Spar Store",
showBackButton: false,
),
//drawer
drawer: getDrawer(context, scaffoldKey),
floatingActionButton: FloatingActionButton(
backgroundColor: myAppTheme?.primaryColor,
onPressed: () async {
String? barcode = await scan();
if (barcode != null && barcode != "") {
if (mapProducts!.containsKey(barcode)) {
//Increase count
setState(() {
mapProducts![barcode] = ++mapProducts![barcode];
});
} else {
//Add a new product
setState(() {
mapProducts![barcode] = 1;
});
}
userProfile["Unordered Products"] = mapProducts;
//Update the new Unordered Products list to the Firebase CLoud Firestpre
await authService.setData();
}
},
child: Icon(Icons.add, color: myAppTheme?.iconTheme.color),
),
//Body
body: mapProducts != null && mapProducts!.length > 0
? Column(
children: <Widget>[
//Products in Cart List
getUnorderedProducts(size),
//Final invoice
getInvoice(size),
//Checkout Button
getCheckoutButton(size),
],
)
: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset(
"assets/images/empty-cart.png",
fit: BoxFit.fitWidth,
),
Text(
"No Items Added.\nPlease Add items by scanning them",
style: myAppTheme?.textTheme.caption,
textAlign: TextAlign.center,
),
],
),
),
),
);
}
//Get the list of all the Orders still in Cart
getUnorderedProducts(double size) {
return SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 0.7,
child: ListView.builder(
itemCount: mapProducts!.length,
itemBuilder: (context, index) {
return FutureBuilder(
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null || snapshot.hasData == false) {
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
myAppTheme!.primaryColor),
strokeWidth: 5,
),
);
} else {
Map<String, dynamic>? mapProduct = snapshot.data;
return GestureDetector(
child: Card(
elevation: myAppTheme?.cardTheme.elevation,
color: myAppTheme?.cardTheme.color,
shape: myAppTheme?.cardTheme.shape,
child: Row(
children: <Widget>[
mapProduct!.containsKey("photo url")
? ClipRRect(
borderRadius: BorderRadius.circular(15),
child: SizedBox(
width: 120,
child: Image.network(
mapProduct["photo url"][0])))
: Icon(
Icons.category,
color: myAppTheme?.iconTheme.color,
),
SizedBox(
width: size - 150,
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
//Title
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
mapProduct["name"],
style: myAppTheme?.textTheme.caption,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.right,
),
),
//Model
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(mapProduct["model"],
style: myAppTheme?.textTheme.bodyText1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.right),
),
//Discount
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"Discount: ${mapProduct["discount"]}%",
style: myAppTheme?.textTheme.bodyText1
?.copyWith(
fontWeight: FontWeight.bold),
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.right),
),
//Price
Padding(
padding: const EdgeInsets.all(8.0),
child: Text("Price: ${mapProduct["price"]}",
style: myAppTheme?.textTheme.bodyText1
?.copyWith(
fontWeight: FontWeight.bold),
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.right),
),
//Quantity
Container(
padding: const EdgeInsets.all(8.0),
height: 60,
child: StepperTouch(
initialValue:
mapProducts?.values.elementAt(index),
direction: Axis.horizontal,
withSpring: true,
primaryColor:
myAppTheme?.colorScheme.secondary,
textColor:
myAppTheme?.textTheme.bodyText1?.color,
onChanged: (int value) {
if (value == 0) {
setState(() {
mapProducts?.remove(mapProducts?.keys
.elementAt(index));
});
} else if (value > 0) {
setState(() {
mapProducts![mapProducts!.keys
.elementAt(index)] = value;
});
}
userProfile["Unordered Products"] =
mapProducts;
},
),
),
],
),
),
],
),
),
//Open
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ViewProductScreen(mapProduct)),
);
},
);
}
},
future: getProduct(mapProducts!.keys.elementAt(index)),
);
}),
);
}
//Get the Total Price and the Total discount
getInvoice(double size) {
return FutureBuilder(
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot == null ||
snapshot.data == null ||
snapshot.hasData == false) {
return Center(
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation<Color>(myAppTheme!.primaryColor),
strokeWidth: 5,
),
);
} else {
Map mapProduct = snapshot.data;
double dbTotalDiscount = mapProduct["discount"],
dbTotalPrice = mapProduct["price"];
return Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
//Total Price
Padding(
padding: const EdgeInsets.fromLTRB(0, 5, 10, 5),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text(
"Total Price: ",
style: myAppTheme?.textTheme.bodyText1,
overflow: TextOverflow.ellipsis,
),
Text(
dbTotalPrice.toString(),
style: myAppTheme?.textTheme.bodyText1,
overflow: TextOverflow.ellipsis,
),
],
),
),
],
);
}
},
future: getInvoiceDetails(),
);
}
getInvoiceDetails() async {
double dbTotalDiscount = 0.0, dbTotalPrice = 0.0;
//Loop through all the products to get the Discount and the Price
await Future.forEach(mapProducts!.keys, (key) async {
String strProductID = key;
Map<String, dynamic>? mapProduct =
(await getProduct(strProductID)) as Map<String, dynamic>?;
if (mapProduct!.containsKey("discount")) {
dbTotalDiscount += double.tryParse(
(mapProduct["discount"] * mapProducts![key]).toString())!;
}
if (mapProduct.containsKey("price")) {
dbTotalPrice += double.tryParse(
(mapProduct["price"] * mapProducts![key]).toString())!;
}
// print("discount: " + dbTotalDiscount.toString());
// print("price: " + dbTotalPrice.toString());
});
return {"discount": dbTotalDiscount, "price": dbTotalPrice};
}
//Get Checkout button
getCheckoutButton(double size) {
return ButtonTheme(
minWidth: double.infinity,
child: Padding(
padding: const EdgeInsets.fromLTRB(80, 20, 80, 10),
child: primaryRaisedButton(
context: context,
text: "Checkout now",
color: myAppTheme?.primaryColor,
onPressed: () async {
//Go to Checkout
await Navigator.push(
context,
MaterialPageRoute(builder: (context) => CheckoutScreen()),
);
setState(() {
//Refresh the UI after returning back from the Checkout screen
mapProducts = userProfile["Unordered Products"];
});
},
),
),
);
}
//=================================================================================
//Get details of product
Future<Map<String, dynamic>? Function()> getProduct(String barcode) async {
return (await FirebaseFirestore.instance
.collection("Products")
.doc(barcode)
.get())
.data;
}
//Scan QR code
Future<String?> scan() async {
try {
String barcode = (await FlutterBarcodeScanner.scanBarcode(
'#ff6666', 'Cancel', true, ScanMode.QR));
return barcode;
} on PlatformException catch (e) {
if (e.code == BarcodeScanner.cameraAccessDenied) {
showSnackBar(
scaffoldKey: scaffoldKey,
text: "The user did not grant the camera permission!",
buttonText: '',
onPressed: () {});
} else {
showSnackBar(
scaffoldKey: scaffoldKey,
text: "Unknown error: $e",
buttonText: '',
onPressed: () {});
}
return null;
} on FormatException {
showSnackBar(
scaffoldKey: scaffoldKey,
text:
'null (User returned using the "back"-button before scanning anything. Result)',
buttonText: '',
onPressed: () {});
} catch (e) {
showSnackBar(
scaffoldKey: scaffoldKey,
text: "Unknown error: $e",
buttonText: '',
onPressed: () {});
}
return null;
}
}
If anyone can help would be great as i am a beginner and it is for a school project.
replace your getProduct() method with this:
Future<Map<String, dynamic>?> getProduct(String barcode) async {
return (await FirebaseFirestore.instance
.collection("Products")
.doc(barcode)
.get())
.data() ;
}
I was designing an audio player, the slider isn't working and the audio keeps on playing even when I press pause. I have attached the code and log.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:geolocator/geolocator.dart';
import 'package:image_picker/image_picker.dart';
import 'package:intl/intl.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/services.dart';
import 'package:video_player/video_player.dart';
import 'package:audioplayers/audioplayers.dart';
import '../login.dart';
import 'package:flutter/services.dart';
class bestPractises extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _bestPractises();
}
}
class _bestPractises extends State<bestPractises> {
VideoPlayerController _controller;
Future<void> _initializeVideoPlayerFuture;
AudioPlayer audioPlayer= new AudioPlayer();
Duration duration = new Duration();
Duration position = new Duration();
bool playing = false;
String Lat = "",
Lng = "";
List<DropdownMenuItem> primtopicsitems = [];
String slctprimtopics = "",slcttopics="";
List<ImageProductSaleModel> imageList = new List();
List<UImodel> bpListUIModel = new List();
List<UImodel> topicListUIModel = new List();
List<DropdownMenuItem> bestpractitems = [],topicitems=[];
File farmerImageFile;
String AudioBase64 = "";
String audioPath = "";
bool isRecord = false;
TextEditingController remarksController = new TextEditingController();
TextEditingController txtmesgController = new TextEditingController();
TextEditingController precadvicController = new TextEditingController();
File fileMedia;
MediaSource source;
String vedio64 = "";
void initState() {
super.initState();
print("inistate");
initvalues();
getLocation();
// _controller = VideoPlayerController.asset(dataSource);
//_initializeVideoPlayerFuture = _controller.initialize();
//_controller.setLooping(true);
// getClientData();
}
void dispose() {
//_controller.dispose();
super.dispose();
}
Future<void> initvalues() async {
List bpList = await db.RawQuery('select * from animalCatalog where catalog_code=\'140\'');
print('bpList ' + bpList.toString());
bpListUIModel = new List();
bestpractitems.clear();
for (int i = 0; i < bpList.length; i++) {
String ptName = bpList[i]["property_value"].toString();
String ptCode = bpList[i]["DISP_SEQ"].toString();
var uimodel = new UImodel(ptName, ptCode);
bpListUIModel.add(uimodel);
setState(() {
bestpractitems.add(DropdownMenuItem(
child: Text(ptName),
value: ptCode,
));
});
}
List topicList = await db.RawQuery('select * from animalCatalog where catalog_code=\'140\'');
print('topicList ' + topicList.toString());
topicListUIModel = new List();
topicitems.clear();
for (int i = 0; i < topicList.length; i++) {
String topicName = topicList[i]["property_value"].toString();
String topicCode = topicList[i]["DISP_SEQ"].toString();
var uimodel = new UImodel(topicName, topicCode);
topicListUIModel.add(uimodel);
setState(() {
topicitems.add(DropdownMenuItem(
child: Text(topicName),
value: topicCode,
));
});
}
}
Future<bool> _onBackPressed() {
return Alert(
context: context,
type: AlertType.warning,
title: "Cancel",
desc: "Are you sure want to cancel?",
buttons: [
DialogButton(
child: Text(
"Yes",
style: TextStyle(color: Colors.white, fontSize: 20),
),
onPressed: () {
Navigator.pop(context);
Navigator.pop(context);
},
width: 120,
),
DialogButton(
child: Text(
"No",
style: TextStyle(color: Colors.white, fontSize: 20),
),
onPressed: () {
Navigator.pop(context);
},
width: 120,
)
],
).show() ??
false;
}
void getLocation() async {
bool isLocationEnabled = await Geolocator.isLocationServiceEnabled();
if (isLocationEnabled) {
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
print("latitude :" +
position.latitude.toString() +
" longitude: " +
position.longitude.toString());
setState(() {
Lat = position.latitude.toString();
Lng = position.longitude.toString();
});
} else {
Alert(
context: context,
title: "Information",
desc: "GPS Location not enabled",
buttons: [
DialogButton(
child: Text(
"OK",
style: TextStyle(color: Colors.white, fontSize: 18),
),
onPressed: () {
Navigator.pop(context);
Navigator.pop(context);
},
color: Colors.green,
)
]).show();
}
}
Widget build(BuildContext context) {
return SafeArea(
child: WillPopScope(
onWillPop: _onBackPressed,
child: Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
centerTitle: true,
leading: IconButton(
icon: Icon(Icons.arrow_back, color: Colors.white),
onPressed: () {
_onBackPressed();
}),
title: Text(
'Best Practices',
style: new TextStyle(
color: Colors.white,
fontSize: 18.0,
fontWeight: FontWeight.w700),
),
iconTheme: IconThemeData(color: Colors.white),
backgroundColor: Colors.green,
brightness: Brightness.light,
),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: ListView(
padding: EdgeInsets.all(10.0),
children: _getbpListings(
context), // <<<<< Note this change for the return type
),
flex: 8,
),
])),
),
),
);
}
List<Widget> _getbpListings(BuildContext context) {
List listings = List<Widget>();
listings.add(txt_label(
"Primary Topic", Colors.black, 16.0, false));
listings.add(singlesearchDropdown(
itemlist: bestpractitems,
selecteditem: slctprimtopics,
hint: "Select the Primary Topic",
onChanged: (value) {
setState(() {
slctprimtopics = value;
});
}));
listings.add(txt_label(
"Topic", Colors.black, 16.0, false));
listings.add(singlesearchDropdown(
itemlist: topicitems,
selecteditem: slcttopics,
hint: "Select the Topic",
onChanged: (value) {
setState(() {
slcttopics = value;
});
}));
// listings.add(txt_label(
// "\nText Messages ", Colors.black, 16.0, false));
listings.add(Row(
children: <Widget>[
Expanded(
child: Row(
children: <Widget>[
Expanded(
flex: 3,
child: txt_label(
"\nText Messages ", Colors.black, 16.0, false)
//),
),
Expanded(
flex: 4,
child: txt_label(
"\nBuyer 1 ", Colors.black, 16.0, false)
),
],
),
),
],
),
);
// listings
// .add(txtfield_dynamic("", txtmesgController, true));
listings.add(Row(
children: <Widget>[
Expanded(
child: Row(
children: <Widget>[
Expanded(
flex: 3,
child: txt_label(
"\nPrecautionary \n Advices ", Colors.black, 16.0, false)
//),
),
Expanded(
flex: 4,
child: txt_label(
"\n This is the Advisory Message ", Colors.black, 16.0, false)
),
],
),
),
],
),
);
// listings.add(txt_label(
// "Precautionary Advices", Colors.black, 16.0, false));
//
// listings
// .add(txtfield_dynamic("", precadvicController, true));
listings.add(Row(
children: <Widget>[
Expanded(
child: Row(
children: <Widget>[
Expanded(
flex: 3,
child: txt_label(
"\nManuals ", Colors.black, 16.0, false)
//),
),
Expanded(
flex: 4,
child: txt_label(
"\n This is the Advisory Message ", Colors.black, 16.0, false)
),
],
),
),
],
),
);
// listings.add(txt_label(
// "Manuals ", Colors.black, 16.0, false));
//
// listings
// .add(txtfield_dynamic("", precadvicController, true));
listings.add(txt_label("\nPhotos of the Advices", Colors.black, 16.0, false));
listings.add( Row(
children: [
Image(
image: AssetImage('images/1.jpg'),
height: 375,
width: 375,
),
const SizedBox(height: 24),
],
),
);
/* listings.add( Row(
FutureBuilder (
future: _initializeVideoPlayerFuture,
builder: (context,snapshot)
{
if(snapshot.connectionState == ConnectionState.done)
{
return AspectRatio(aspectRatio: _controller.value.aspectRatio,
child: VideoPlayer(_controller),
);
}else {
return Center(
child : CircularProgressIndicator(),
);
}
},
),
floatingActionButton: FloatingActionButton(
onpressed : (){
setState(() {
if(_controller.value.isPlaying) {
_controller.pause();
} else {
_controller.play();
}
});
}
child:
Icon(_controller.value.isPlaying ? Icons.pause : Icons.play_arrow),
),
),
); */
// listings.add(img_picker(
// label: "Photos of the Advices \*",
// onPressed: () {
// getImage();
// },
// filename: farmerImageFile,
// ondelete: () {
// ondelete();
// }));
listings.add(txt_label("Video", Colors.black, 16.0, false));
listings.add( Row(
children: [
RaisedButton(
child: Text('\Video'),
onPressed: () => capture(MediaSource.video),
color: Colors.green,
textColor: Colors.white,
),
const SizedBox(height: 24),
Expanded(
child: fileMedia == null
? Container()
: (source == MediaSource.video
? Column(
children: [
VideoWidget(fileMedia),
],
)
: Container()),
),
],
),
);
listings.add(txt_label("\nAudio", Colors.black, 16.0, false));
listings.add(Container(
color: Colors.white,
child: Column(
children: <Widget> [
//Image(image: AssetImage(''),),
slider(),
InkWell(
onTap: () {
getAudio();
},
child:Icon(
playing == false
? Icons.play_circle_outline
: Icons.pause_circle_outline,
size: 50,
color: Colors.blue,
),
)
],
),
),
);
listings.add(btn_dynamic(
label: "Audio",
bgcolor: Colors.green,
txtcolor: Colors.white,
fontsize: 18.0,
centerRight: Alignment.centerLeft,
margin: 10.0,
btnSubmit: () async {
audioPath = await Navigator.push(context,
MaterialPageRoute(builder: (context) => AudioRecorder()));
File audioFile = new File(audioPath);
List<int> fileBytes = await audioFile.readAsBytes();
AudioBase64 = base64Encode(fileBytes);
setState(() {
isRecord = true;
});
}));
if (isRecord) {
listings.add(Container(
color: Colors.blueGrey,
height: 40,
child: Center(
child: Row(
children: [
Expanded(
child: Center(
child: txt_label(
audioPath.split('/').last, Colors.white, 14.0, false)),
flex: 3,
),
Expanded(
child: InkWell(
onTap: () {
setState(() {
isRecord = false;
});
},
child: Icon(Icons.delete, color: Colors.red)),
flex: 1,
)
],
),
),
));
}
listings.add(txt_label("\nRemarks", Colors.black, 14.0, false));
listings.add(txtfield_dynamic("Remarks", remarksController, true));
/* listings.add(Container(
child: Row(
children: [
Expanded(
flex: 1,
child: Container(
padding: EdgeInsets.all(3),
child: RaisedButton(
child: Text(
'Cancel',
style: new TextStyle(color: Colors.white, fontSize: 18),
),
onPressed: () {
_onBackPressed();
},
color: Colors.red,
),
),
),
Expanded(
flex: 1,
child: Container(
padding: EdgeInsets.all(3),
child: RaisedButton(
child: Text(
'Submit',
style: new TextStyle(color: Colors.white, fontSize: 18),
),
onPressed: () {
_bestpractisessubmit();
},
color: Colors.green,
),
),
),
//
],
),
)); */
return listings;
}
Widget slider() {
return Slider.adaptive(
min: 0.0,
value: position.inSeconds.toDouble(),
max: duration.inSeconds.toDouble(),
onChanged : (double value) {
setState(() {
audioPlayer.seek(new Duration(seconds: value.toInt()));
});
}
);
}
void getAudio() async {
var url = "https://www.learningcontainer.com/wp-content/uploads/2020/02/Kalimba.mp3";
if (playing) {
var res = await audioPlayer.pause();
if(res == 1) {
setState(() {
playing = false;
});
}
} else {
var res = await audioPlayer.play(url,isLocal: true);
if(res == 1) {
setState(() {
playing = true;
});
}
}
audioPlayer.onDurationChanged.listen((Duration dd) {
setState(() {
duration = dd;
});
});
audioPlayer.onAudioPositionChanged.listen((Duration dd) {
setState(() {
position = dd;
});
});
}
void _bestpractisessubmit() async{
if (slctprimtopics == "") {
errordialog(context, "Information",
"Primary Topic should not be empty");
}else if (slcttopics == ''){
errordialog(context, "Information",
"Topic should not be empty");
}else{
Alert(
context: context,
type: AlertType.info,
title: "Information",
desc: "Are you sure want to proceed?",
buttons: [
DialogButton(
child: Text(
"Yes",
style: TextStyle(color: Colors.white, fontSize: 20),
),
onPressed: () {
Navigator.pop(context);
Navigator.pop(context);
Navigator.pop(context);
},
width: 120,
),
DialogButton(
child: Text(
"No",
style: TextStyle(color: Colors.white, fontSize: 20),
),
onPressed: () {
Navigator.pop(context);
},
width: 120,
)
],
).show() ??
false;
}
}
Future getImage() async {
var image = await ImagePicker.platform
.pickImage(source: ImageSource.camera, imageQuality: 30);
setState(() {
farmerImageFile = new File(image.path);
});
}
void ondelete() {
setState(() {
if (farmerImageFile != null) {
setState(() {
farmerImageFile = null;
});
}
});
}
Future capture(MediaSource source) async {
setState(() {
this.source = source;
this.fileMedia = null;
});
final result = await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => SourcePage(),
settings: RouteSettings(
arguments: source,
),
),
);
if (result == null) {
return;
} else {
setState(() {
fileMedia = result;
});
}
}
}
log:
[ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception:
setState() called after dispose(): _bestPractises#e42c5(lifecycle
state: defunct, not mounted)
This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent
widget no longer includes the widget in its build). This error can
occur when code calls setState() from a timer or an animation
callback.
The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check
the "mounted" property of this object before calling setState() to
ensure the object is still in the tree.
This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State
object after it has been removed from the tree. To avoid memory leaks,
consider breaking the reference to this object during dispose().
#0 State.setState. (package:flutter/src/widgets/framework.dart:1052:9)
#1 State.setState (package:flutter/src/widgets/framework.dart:1087:6)
#2 _bestPractises.getAudio. (package:aftab/Screens/bestPractisesscreen.dart:611:7)
#3 _rootRunUnary (dart:async/zone.dart:1362:47)
#4 _CustomZone.runUnary (dart:async/zone.dart:1265:19)
#5 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1170:7)
#6 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:341:11)
#7 _DelayedData.perform (dart:async/stream_impl.dart:591:14)
#8 _StreamImplEvents.handleNext (dart:async/stream_impl.dart:706:11)
#9 _PendingEvents.schedule. (dart:async/stream_impl.dart:663:7)
#10 _rootRun (dart:async/zone.dart:1346:47)
#11 _CustomZone.run (dart:async/zone.dart:1258:19)
#12 _CustomZone.runGuarded (dart:async/zone.dart:1162:7)
#13 _CustomZone.bindCallbackGuarded. (dart:async/zone.dart:1202:23)
#14 _rootRun (dart:async/zone.dart:1354:13)
#15 _CustomZone.run (dart:async/zone.dart:1258:19)
#16 _CustomZone.runGuarded (dart:async/zone.dart:1162:7)
#17 _CustomZone.bindCallbackGuarded. (dart:async/zone.dart:1202:23)
#18 _microtaskLoop (dart:async/schedule_microtask.dart:40:21)
#19 _startMicrotaskLoop (dart:async/schedule_microtask.dart:49:5)
I resolved my current issue regarding locking and unlocking buttons. I am basing my levels on the marks the user got. If they got a score of 25 and above, they will proceed to the next level.
Now the problem here is since my quiz is sharing one code with a different JSON file, when the user perfects the score on the first stage, the whole level will unlock which is a no-no. The idea is even they scored perfect, the next stage will be unlocked not the whole level. I tried thinking of ways on how to do it but nothing comes to my mind.
Here is my Quiz Page:
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'package:baybay_app/Quiz/NextLevel class.dart';
import 'package:baybay_app/Quiz/QuizHome.dart';
import 'package:baybay_app/Quiz/ResultPage.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class Quizjson extends StatelessWidget {
String roundName;
bool isComplete = false;
Quizjson(this.roundName, mark);
String assettoload;
int question;
// a function
// sets the asset to a particular JSON file
// and opens the JSON
setasset() {
if (roundName == 'Easy Round') {
assettoload ='assets/Sample.json';
} else if (roundName == 'Average Round') {
assettoload = 'assets/Medium.json';
} else if (roundName == 'Hard Round') {
assettoload = 'assets/Hard.json';
}else {
assettoload = 'assets/Intermediate.json';
}
}
#override
Widget build(BuildContext context) {
setasset();
return FutureBuilder(
future: DefaultAssetBundle.of(context).loadString(assettoload, cache: false),
builder: (context, snapshot){
List mydata = json.decode(snapshot.data.toString());
if(mydata == null){
return Scaffold(
body: Center(
child: Text(
"Loading",
),
),
);
}else{
return quizpage(mydata: mydata);
}
}
);
}
}
class quizpage extends StatefulWidget {
String langname;
var mydata;
quizpage({Key key, #required this.mydata}): super(key: key);
#override
_quizpageState createState() => _quizpageState(mydata);
}
class _quizpageState extends State<quizpage> {
var mydata;
_quizpageState(this.mydata);
#override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitDown, DeviceOrientation.portraitUp]);
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
flex: 3,
child: Container(
padding: EdgeInsets.all(20.0),
alignment: Alignment.bottomLeft,
child: Text(mydata[0][question.toString()])
),
),
Expanded(
flex: 6,
child: Container(
child: Column(
children: [
Row(
children:[
ChoiceButton("a"),
ChoiceButton("b")
]
),
Row(
children: [
ChoiceButton("c"),
ChoiceButton("d"),
]
)
]
),
),
),
Expanded(
flex: 1,
child: Container(
alignment: Alignment.topCenter,
child: Center(
child: Text(
showtimer,
style: TextStyle(
fontSize: 20.0
),
),
),
),
),
],
)
);
}
Widget ChoiceButton(String k) {
return Padding(
padding: EdgeInsets.symmetric(
vertical: 10.0,
horizontal: 10.0),
child: MaterialButton(
onPressed: ()=>CheckAnswer(k),
child: Text(
mydata[1][question.toString()][k],
style: TextStyle(
color: Colors.white
)),
color: btncolor[k],
),
);
}
Color colorsToShow = Colors.brown[700];
Color right = Colors.greenAccent[700];
Color wrong = Colors.redAccent[700];
int mark = 0;
int question = 1;
int timer = 30;
String showtimer = "30";
bool canceltimer = false;
bool isComplete = false;
Map<String,Color> btncolor = {
"a" : Colors.brown[700],
"b" : Colors.brown[700],
"c" : Colors.brown[700],
"d" : Colors.brown[700],
};
#override
void initState(){
starttimer();
super.initState();
}
#override
void setState(fn){
if(mounted){
super.setState(fn);
}
}
void starttimer() async {
const onesec = Duration(seconds: 1);
Timer.periodic(onesec, (Timer t){
setState(() {
if(timer < 1){
t.cancel();
NextQuestion();
}
else if(canceltimer==true){
t.cancel();
}
else{
timer = timer - 1;
}
showtimer = timer.toString();
});
});
}
void NextQuestion(){
canceltimer = false;
timer = 30;
setState(() {
if(question< 10){
question++;
}
else{
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => ResultPage(mark: mark),
));}
btncolor["a"] = Colors.brown[700];
btncolor["b"] = Colors.brown[700];
btncolor["c"] = Colors.brown[700];
btncolor["d"] = Colors.brown[700];
isComplete = true;
});
starttimer();
}
void CheckAnswer(String k) {
if(mydata[2][question.toString()] == mydata[1][question.toString()][k]){
mark = mark+5;
colorsToShow = right;
}
else{
colorsToShow = wrong;
}
setState(() {
btncolor[k] = colorsToShow;
});
Timer(Duration(seconds: 2), NextQuestion);
}
}
Here is my Quiz Home:
import 'package:baybay_app/Quiz/QuizTracingSound.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:baybay_app/Quiz/QuizTracingSound.dart';
class QuizHome extends StatefulWidget {
int mark = 0;
QuizHome({ #required this.mark });
#override
_QuizHomeState createState() => _QuizHomeState(mark);
}
class _QuizHomeState extends State<QuizHome> {
createAlertDialouge(BuildContext context){
return showDialog(context: context, builder: (context){
return AlertDialog(
title: Text('Not Complete'),
content: Text('Please score 25 above mark'),
actions: [
Center(
child: RaisedButton(
onPressed: (){Navigator.of(context).pop();},
color: Colors.grey[100],
),
),
],
shape: RoundedRectangleBorder(side: BorderSide(width: 16.0, color: Colors.red.shade100)),
backgroundColor: Colors.lightBlue.shade100,
);
});
}
createAlertDialougeOne(BuildContext context){
return showDialog(context: context, builder: (context){
return AlertDialog(
title: Text('Not Complete'),
content: Text('Please score 35 and above mark'),
actions: [
Center(
child: RaisedButton(
onPressed: (){Navigator.of(context).pop();},
color: Colors.grey[100],
),
),
],
shape: RoundedRectangleBorder(side: BorderSide(width: 16.0, color: Colors.red.shade100)),
backgroundColor: Colors.lightBlue.shade100,
);
});
}
createAlertDialougeTwo(BuildContext context){
return showDialog(context: context, builder: (context){
return AlertDialog(
title: Text('Not Complete'),
content: Text('Please score 35 and above mark'),
actions: [
Center(
child: RaisedButton(
onPressed: (){Navigator.of(context).pop();},
color: Colors.grey[100],
),
),
],
shape: RoundedRectangleBorder(side: BorderSide(width: 16.0, color: Colors.red.shade100)),
backgroundColor: Colors.lightBlue.shade100,
);
});
}
int mark = 0 ;
_QuizHomeState(this.mark);
String langname;
bool isComplete = false;
#override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitDown, DeviceOrientation.portraitUp]);
List <String> images = [
'assets/Ba.gif',
'assets/GA.PNG',
'assets/HA.PNG',
'assets/SA.PNG'
];
return Scaffold(
appBar: AppBar(
title: Text('Quiz Home '),
),
body:ListView(
children: <Widget>[
FlatButton(
child: CustomCard('Easy Round', images[0], des[1], isComplete, Colors.green, mark),
onPressed: () {
{ Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (context)=> Quizjson("Easy Round", mark = 5),
));}
}
),
SizedBox(
height: 20.0,
),
FlatButton(
child: CustomCard('Average Round', images[1], des[1], isComplete, lockOrNot, mark),
onPressed: _onPressedOne,
),
SizedBox(
height: 20.0,
),
FlatButton(
onPressed: _onPressedTwo,
child: CustomCard('Hard Round', images[2],des[2], isComplete, btncolor, mark)
),
SizedBox(
height: 20.0,
),
FlatButton(
onPressed: _onPressedThree,
child: CustomCard('Intermediate Round', images[3],des[3], isComplete, btncolor, mark )
),
],
)
);
}
List <String> des = [
"The easy round lets you test your knowledge in determining letters. Are you up for the challenge? Click here!!!",
"Do you want to step up your baybayin game? Let's see if you can distinguish words! Click here!!!",
"Do you know how to construct sentences with the use of Baybayin? Click here!!!",
"Masters of baybayin can only enter this quiz! Are you one of them? Click Here!!!",
];
Color btncolor;
Widget CustomCard(String roundName,images,String des, bool isComplete, Color btncolor, int mark ) {
return Material(
color: btncolor,
elevation: 10.0,
borderRadius: BorderRadius.circular(20.0),
child: Container(
child: Column(
children: <Widget>[
Text(
'$mark'
),
Padding(
padding: EdgeInsets.symmetric(
vertical: 10.0,
),
child: Material(
elevation: 5.0,
borderRadius: BorderRadius.circular(100.0),
child: Container(
height: 200.0,
width: 200.0,
child:ClipOval(
child: Image(
image: AssetImage(images),
),
)
),
),
),
Center(
child: Text( roundName,
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
letterSpacing: 10.0,
fontFamily: 'S'
),),
),
Container(
padding:EdgeInsets.all(8.0),
child: Text(
des,
style: TextStyle(
color: Colors.white,
letterSpacing: 1.0
),
),
),
],
),
),
);
}
Color unlockColor = Colors.green;
Color lockColor = Colors.grey;
Color lockOrNot = Colors.grey;
void _onPressedOne() {
int marks1 = 24;
if(marks1< mark){
Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (context)=> Quizjson( "Average Round", 7 ),
));
}
else{
createAlertDialouge(context);
}
}
void _onPressedTwo() {
int marks2 = 34;
int marc = mark;
if( marks2 < marc ){
Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (context)=> Quizjson("Hard Round", 7 ),
));
}
else{
createAlertDialougeOne(context);
}
}
void _onPressedThree() {
int marks3 = 49;
int marc = mark;
if( marks3 < marc ){
Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (context)=> Quizjson("Average Round", 8),
));
}
else{
createAlertDialougeTwo(context);
}
}
}
If you need the Result page:
import 'package:baybay_app/Quiz/QuizHome.dart';
import 'package:flutter/material.dart';
class ResultPage extends StatefulWidget {
int mark;
ResultPage({Key key, #required this.mark}) : super(key: key);
#override
_ResultPageState createState() => _ResultPageState(mark);
}
class _ResultPageState extends State<ResultPage> {
List<String> images = [
'assets/excellent.jpg',
'assets/good.png',
'assets/Sorry.png'
];
String message;
String image;
#override
void initState(){
if(mark<5){
image = images[2];
message = 'Try Again..\n' + 'You scored $mark';
}
else if(mark==5){
image = images[1];
message = 'Good.. \n' + 'You scored $mark';
}
else{
image = images[0];
message = 'Excellent!!!...\n' + 'You scored $mark';
}
}
int mark;
_ResultPageState(this.mark);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Result"
)
),
body: Column(
children:[
Expanded(
flex: 6,
child: Material(
elevation: 5.0,
child: Container(
child:Column(
children: [
Material(
child: Container(
width: 300.0,
height: 300.0,
child: ClipRect(
child: Image(
image: AssetImage(
image,
)
)
),
),
),
Center(
child: Text(
message,
style: TextStyle(
fontSize: 20.0,
fontFamily: 'Staatliches'
),
),
)
],
),
),
),
),
Expanded(
flex:4,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children:<Widget>[
OutlineButton(
onPressed: () {
Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (context)=> QuizHome(mark:mark)));
},
child: Text(
'Continue',
style: TextStyle(
fontSize: 20.0,
fontFamily: 'Staatliches'
)
),
padding: EdgeInsets.symmetric(vertical: 10.0,horizontal: 15.0),
borderSide: BorderSide(width: 3.0,color: Colors.brown[700])
)
],
)
)
]
)
);
}
}
What can I try next?
So I am going to create a quiz app but while I run it, I get the following error
'pakage:flutter/src/wigets/text.dart': Field assertion : line 298 pos
10 :'data !=null': A non-null String must be provided to a Text widget
it is a flutter based code, what are the possible thing i missed ? Can you help me please ! i shall be obliged for this :)
Here Is image of Error
import 'dart:async';
import 'dart:convert';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:crazyquiz/resultpage.dart';
class getjson extends StatelessWidget {
String langname;
getjson(this.langname);
String assettoload;
setasset() {
if (langname == "Funny") {
assettoload = "assets/funny.json";
} else if (langname == "General Knowledge") {
assettoload = "assets/gk.json";
} else if (langname == "Javascript") {
assettoload = "assets/js.json";
} else if (langname == "C++") {
assettoload = "assets/cpp.json";
} else {
assettoload = "assets/linux.json";
}
}
#override
Widget build(BuildContext context) {
setasset();
return FutureBuilder(
future:
DefaultAssetBundle.of(context).loadString(assettoload, cache: true),
builder: (context, snapshot) {
List mydata = json.decode(snapshot.data.toString());
if (mydata == null) {
return Scaffold(
body: Center(
child: Text(
"Loading",
),
),
);
} else {
return quizpage(mydata: mydata);
}
},
);
}
}
class quizpage extends StatefulWidget {
var mydata;
quizpage({Key key, this.mydata = ""}) : super(key: key);
#override
_quizpageState createState() => _quizpageState(mydata);
}
class _quizpageState extends State<quizpage> {
var mydata;
_quizpageState(this.mydata);
Color colortoshow = Colors.indigoAccent;
Color right = Colors.green;
Color wrong = Colors.red;
int marks = 0;
int i = 1;
int j = 1;
int timer = 30;
String showtimer = "30";
var random_array;
Map<String, Color> btncolor = {
"a": Colors.purple,
"b": Colors.lightBlueAccent,
"c": Colors.blueGrey,
"d": Colors.blueAccent,
};
bool canceltimer = false;
genrandomarray() {
var distinctIds = [];
var rand = new Random();
for (int i = 0;;) {
distinctIds.add(rand.nextInt(10));
random_array = distinctIds.toSet().toList();
if (random_array.length < 10) {
continue;
} else {
break;
}
}
print(random_array);
}
#override
void initState() {
starttimer();
genrandomarray();
super.initState();
}
#override
void setState(fn) {
if (mounted) {
super.setState(fn);
}
}
void starttimer() async {
const onesec = Duration(seconds: 1);
Timer.periodic(onesec, (Timer t) {
setState(() {
if (timer < 1) {
t.cancel();
nextquestion();
} else if (canceltimer == true) {
t.cancel();
} else {
timer = timer - 1;
}
showtimer = timer.toString();
});
});
}
void nextquestion() {
timer = 30;
setState(() {
if (j < 10) {
i = random_array[j];
j++;
} else {
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => resultpage(marks: marks),
));
}
btncolor["a"] = Colors.purple;
btncolor["b"] = Colors.lightBlueAccent;
btncolor["c"] = Colors.blueGrey;
btncolor["d"] = Colors.blueAccent;
});
starttimer();
}
void checkanswer(String k) {
if (mydata[2][i.toString()] == mydata[1][i.toString()][k]) {
[i.toString()][k]);
marks = marks + 5;
colortoshow = right;
} else {
}
setState(() {
btncolor[k] = colortoshow;
canceltimer = true;
});
Timer(Duration(seconds: 1), nextquestion);
}
Widget choicebutton(String k) {
return Padding(
padding: EdgeInsets.symmetric(
vertical: 10.0,
horizontal: 20.0,
),
child: MaterialButton(
onPressed: () => checkanswer(k),
child: Text(
mydata[1][i.toString()][k],
style: TextStyle(
color: Colors.white,
fontFamily: "Alike",
fontSize: 16.0,
),
maxLines: 1,
),
color: btncolor[k],
splashColor: Colors.indigo[700],
highlightColor: Colors.indigo[700],
minWidth: 200.0,
height: 45.0,
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
),
);
}
#override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitDown, DeviceOrientation.portraitUp]);
return WillPopScope(
onWillPop: () {
return showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(
"Quizstar",
),
content: Text("You Can't Go Back At This Stage."),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(
'Ok',
),
)
],
));
},
child: Scaffold(
body: Column(
children: <Widget>[
Expanded(
flex: 3,
child: Container(
padding: EdgeInsets.all(15.0),
alignment: Alignment.bottomLeft,
child: Text(
mydata[0][i.toString()],
style: TextStyle(
fontSize: 16.0,
fontFamily: "Quando",
),
),
),
),
Expanded(
flex: 6,
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
choicebutton('a'),
choicebutton('b'),
choicebutton('c'),
choicebutton('d'),
],
),
),
),
Expanded(
flex: 1,
child: Container(
alignment: Alignment.topCenter,
child: Center(
child: Text(
showtimer.toString(),
style: TextStyle(
fontSize: 35.0,
fontWeight: FontWeight.w700,
fontFamily: 'Times New Roman',
),
),
),
),
),
],
),
),
);
}
}`
I think Yalin is perfectly right, but I would add:
Dart has some very useful operators to prevent that kind of errors, which can be catastrofic on real world usage. You can check them here. In particular you could use
mydata[0][i.toString()] ?? "Default text"
to prevent this kind of problem when an object is null
Not a flutter guy. But I'll try to help you.
If you look into flutter source and search your error message you'll get a clue what to do.
So at text.dart we can find that dart check that you fill data field with a String when you call a constructor.
So my bet is that you misplace toString here mydata[0][i.toString()]
Text widget needs a string to be initialized. As vsenik mentioned, you are probably giving a null instead of string to a text widget. The problem is in one of the below lines.
mydata[1][i.toString()][k]
mydata[0][i.toString()]
You could use debug or insert a print statement before these lines.