Related
I am making a quiz app and at first everything works fine, but when I do a quiz the first time, it does the correct or incorrect answer check perfectly.
But when I go back to quiz without restarting the app just navigating from one page to another the PageView does not reset its state again.
Before taking the quiz
enter image description here
After I do the quiz and I want to do it again without restart the app, I get the checked answers.
enter image description here
How to return the PageView to its initial state without restart the app
Here is my code:
import 'package:flutter/material.dart';
import 'package:quizapp/src/models/quiz_model.dart';
import 'package:quizapp/src/screens/result_screen.dart';
class QuizScreen extends StatefulWidget {
const QuizScreen({Key? key}) : super(key: key);
#override
State<QuizScreen> createState() => _QuizScreenState();
}
class _QuizScreenState extends State<QuizScreen> {
int _questionNumber = 1;
late PageController _controller;
int _score = 0;
#override
void initState() {
_controller = PageController(initialPage: 0);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: PageView.builder(
physics: const NeverScrollableScrollPhysics(),
controller: _controller,
itemCount: questions.length,
itemBuilder: (context, index) {
final _question = questions[index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 16,
),
Text(
_question.text,
style: const TextStyle(fontSize: 22),
),
const SizedBox(
height: 16,
),
Expanded(
child: SingleChildScrollView(
child: Column(
children: _question.options
.map((option) => GestureDetector(
onTap: () {
Future.delayed(
const Duration(milliseconds: 250),
() {
if (_questionNumber <
questions.length) {
_controller.nextPage(
duration: const Duration(
milliseconds: 250),
curve: Curves.easeInExpo);
setState(() {
if (option.isCorrect == true) {
_score++;
}
});
setState(() {
_questionNumber++;
// _isLocked = false;
});
} else {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) =>
ResultScreen(
score: _score),
));
}
});
if (_question.isLocked) {
return;
} else {
setState(() {
_question.isLocked = true;
_question.selectedOption = option;
});
}
},
child: Container(
height: 50,
padding: const EdgeInsets.all(12),
margin: const EdgeInsets.symmetric(
vertical: 8),
decoration: BoxDecoration(
color: const Color(0xFF6949FD),
borderRadius:
BorderRadius.circular(16),
border: Border.all(
color: getColorForOption(
option, _question))),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
option.text,
style: const TextStyle(
fontSize: 18,
color: Colors.white),
),
const SizedBox(width: 10),
getIconForOption(option, _question)
],
),
),
))
.toList(),
)))
]);
},
)),
),
],
),
));
}
Color getColorForOption(Option option, Question _question) {
final isSelected = option == _question.selectedOption;
if (_question.isLocked) {
if (isSelected) {
return option.isCorrect ? Colors.green : Colors.red;
} else if (option.isCorrect) {
return Colors.green;
}
}
return const Color(0xFF6949FD);
}
Widget getIconForOption(Option option, Question _question) {
final isSelected = option == _question.selectedOption;
if (_question.isLocked) {
if (isSelected) {
return option.isCorrect
? const Icon(Icons.check_circle, color: Colors.green)
: const Icon(Icons.cancel, color: Colors.red);
} else if (option.isCorrect) {
return const Icon(Icons.check_circle, color: Colors.green);
}
}
return const SizedBox.shrink();
}
}
An easier way is to restart the app when you go back or press a button. You can wrap Scaffold() with WillPopScope() to restart when you back. You can use this package to restart.
If you need to save the score, you can save it in local storage. Another easy package for this is get_storage.
dependencies:
flutter_phoenix: ^1.1.0
runApp(Phoenix(child: const MyApp()));
WillPopScope(
onWillPop: () async {
Phoenix.rebirth(context);
},
child: Scaffold())
I am using flutter Navigator to open a camera preview screen, using camera plugin. We must collect several photos for our business logic, so I am repeatedly calling two screens: CameraPreviewScreen (the camera screen), and ImagePreviewScreen (to confirm and see how is the taken photo). The problem is, after two recursive Navigator calls, I am getting this error:
Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe.
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 dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.
I believe it is something related to key because camera preview is Statefull, I tried a few keys without success, seems that CameraPreviewScreen calls dispose(), and then the BuildContext used for navigation after that is not valid anymore.
How can I avoid this kind of problem?
The code Snippets:
CameraPreviewScreen
[...]
class _VehicleCameraPreviewScreenState extends State<VehicleCameraPreviewScreen>
with WidgetsBindingObserver {
CameraController? _controller;
late CameraDescription _currentCamera;
Future<CameraDescription> _getAvailableCameras() async {
try {
List<CameraDescription> cameras = await availableCameras();
_currentCamera = cameras.firstWhere((camera) {
// get only back camera
return camera.lensDirection == CameraLensDirection.back;
}, orElse: () => cameras.first);
return _currentCamera;
} catch (e) {
OttoLogger.error(e);
rethrow;
}
}
Future<void> _setCurrentCamera(CameraDescription cameraDescription) async {
_controller = CameraController(
cameraDescription,
ResolutionPreset.max,
enableAudio: false,
imageFormatGroup: ImageFormatGroup.yuv420,
);
// If the _controller is updated then update the UI.
_controller?.addListener(() {
if (mounted) setState(() {});
if (_controller?.value.hasError ?? false) {
OttoLogger.info('Camera error ${_controller?.value.errorDescription}');
}
});
final permissions = PermissionService();
if (!await permissions.isPermissionGranted(Permissions.camera)) {
if (!await permissions.requestPermission(Permissions.camera)) {
// goRoute call
context.pushNamed(OttoRoutes.cameraPermissionDenied);
return;
}
}
await _controller?.initialize();
await _controller?.lockCaptureOrientation(DeviceOrientation.portraitUp);
await _controller?.setFlashMode(FlashMode.off);
if (mounted) {
setState(() {});
}
}
Future<void> _initCamera() async {
await _getAvailableCameras();
//We need to check if the controller is not equal to null to dispose it
//Before initialising a new onw to avoid possible memory leak.
if (_controller != null) {
await _controller?.dispose();
}
_setCurrentCamera(_currentCamera);
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_initCamera();
}
#override
void dispose() {
_controller?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
appBar: const CustomAppBar(
brightness: Brightness.light,
backGroundColor: Colors.black,
),
body: (_controller == null || !_controller!.value.isInitialized)
? const Center(child: KCircularProgressLoader())
: Align(
alignment: Alignment.topCenter,
child: CameraPreview(
_controller!,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
width: context.width,
height: 24,
color: Colors.black,
),
const Spacer(),
Container(
padding:
context.insetsSymetric(horizontal: _customPadding),
width: context.width,
color: Colors.black,
child: GestureDetector(
onTap: widget.onCapture != null
? () async {
try {
final image = await _controller!.takePicture();
widget.onCapture!(image.path);
} catch (error) {
print(error);
}
}
: null,
),
],
),
),
),
);
}
}
ImagePreviewScreen
class TakenImagePreviewScreen extends StatelessWidget {
final ImagePreviewArguments imagePreview;
const TakenImagePreviewScreen({
Key? key,
required this.imagePreview,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: Colors.black,
appBar: const CustomAppBar(
brightness: Brightness.light,
),
body: SingleChildScrollView(
child: Padding(
padding: context.insetsOnly(bottom: 150),
child: Center(
child: imagePreview.image ??
Image.file(
File(imagePreview.imagePath!),
fit: BoxFit.contain,
),
),
),
),
bottomSheet: Container(
width: context.width,
decoration: const BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16.0),
topRight: Radius.circular(16.0),
),
color: Colors.white,
),
child: Padding(
padding: context.insetsAll(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
if (imagePreview.title != null)
Text(
imagePreview.title!,
style: Font.h3(context),
),
if (imagePreview.actions != null)
Padding(
padding: context.insetsOnly(bottom: 10),
child: imagePreview.actions!,
),
],
),
),
),
),
);
}
}
I'm trying to add an interstitial ads, but got 'Null check operator used on a null value' error.
Error
E/flutter (21082): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: Null check operator used on a null value
Code problem
onTap: () async {
isInterstitialVideoAvailable =
(await FlutterApplovinMax.isInterstitialLoaded(
listener!))!;
if (isInterstitialVideoAvailable) {
FlutterApplovinMax.showInterstitialVideo(
(AppLovinAdListener? event) => listener!(event));
}
Main code involved
class CollectionCard extends StatefulWidget {
const CollectionCard();
#override
State<CollectionCard> createState() => _CollectionCardState();
}
class _CollectionCardState extends State<CollectionCard> {
AppLovinListener? get listener => null;
// AppLovinListener get listener => null;
void initState() {
super.initState();
FlutterApplovinMax.initInterstitialAd('91b26a5859e1b480');
}
bool isInterstitialVideoAvailable = false;
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
/*24 is for notifications bar on Android */
final double itemHeight = (size.height - kToolbarHeight - 28) / 2;
final double itemWidth = size.width / 4;
// var len = listener?.length ?? 0;
return Container(
child: Padding(
padding: const EdgeInsets.fromLTRB(10.0, 0.0, 10.0, 10.0),
child: Column(
children: <Widget>[
GridView.count(
primary: true,
padding: const EdgeInsets.fromLTRB(20, 0, 20, 20),
crossAxisSpacing: 10, //Reduce Horizontal Spacing
mainAxisSpacing: 10, //Reduce Vertical Spacing
crossAxisCount: 3,
physics: ScrollPhysics(),
childAspectRatio: (6 / 8),
// (itemWidth / itemHeight),
shrinkWrap: true,
children: <Widget>[
Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
elevation: 2,
color: Theme.of(context).scaffoldBackgroundColor,
child: InkWell(
onTap: () async {
isInterstitialVideoAvailable =
(await FlutterApplovinMax.isInterstitialLoaded(
listener!))!;
if (isInterstitialVideoAvailable) {
FlutterApplovinMax.showInterstitialVideo(
(AppLovinAdListener? event) => listener!(event));
}
Navigator.push(
context,
MaterialPageRoute(
builder: (ctx) => LearnPage(),
),
);
},
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ImageIcon(
AssetImage('assets/icons/learn.png'),
color: kLightPrimary,
size: 60,
), // Icon(
// layout.icon,
// size: 40,
// ),
Text(
'Learn',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
),
Can anyone provide a solution for this?
You have to init a Interstitial Ads before use it,
Example:
Init Ads on initState()
InterstitialAd? _interstitialAd;
int _countMaxAdFailedToLoad = 3;
int _countAdInitialToLoad = 0;
#override
void initState() {
super.initState();
_createAds();
}
void _createAds(){
InterstitialAd.load(
adUnitId: Platform.isAndroid
? 'ca-app-pub-3940256099942544/1033173712'
: 'ca-app-pub-3940256099942544/4411468910',
request: AdRequest(),
adLoadCallback: InterstitialAdLoadCallback(
onAdLoaded: (InterstitialAd ad) {
_interstitialAd = ad;
_countAdInitialToLoad = 0;
},
onAdFailedToLoad: (LoadAdError error) {
print('InterstitialAd failed to load: $error');
_countAdInitialToLoad += 1;
_interstitialAd = null;
if (_countAdInitialToLoad >= _countMaxAdFailedToLoad) {
_createInterstitialAds();
}
},
),
);
}
And you can show this ads.
onTap: (){
if (_interstitialAd != null) {
_interstitialAd?.fullScreenContentCallback =
FullScreenContentCallback(
onAdDismissedFullScreenContent: (InterstitialAd ad) {
print('$ad onAdDismissedFullScreenContent.');
ad.dispose();
_createAds();
},
onAdFailedToShowFullScreenContent:
(InterstitialAd ad, AdError error) {
print('$ad onAdFailedToShowFullScreenContent: $error');
ad.dispose();
_createAds();
});
_interstitialAd?.show();
}
}
i am trying to monitize my app using admob but its really hard to implement it in the app, now everything is set but it gives the following error,"The method 'load' was called on null.
Receiver: null
Tried calling: load()"
can anyone help?
import 'package:carousel_pro/carousel_pro.dart';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:animated_dialog/animated_dialog.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:firebase_admob/firebase_admob.dart';
class Car extends StatefulWidget {
#override
_CarState createState() => _CarState();
}
class _CarState extends State<Car> {
static String adId = 'ca-app-pub-xxxxx92680942917/5470xxxxxx';
String appId = 'ca-app-pub-xxxxxx2680942917~612810xxxx';
MobileAdTargetingInfo targetingInfo;
BannerAd myBanner;
#override
void initState() {
super.initState();
MobileAdTargetingInfo(
keywords: <String>['flutterio', 'beautiful apps'],
contentUrl: 'https://flutter.io',
birthday: DateTime.now(),
childDirected: false,
designedForFamilies: false,
gender: MobileAdGender
.male, // or MobileAdGender.female, MobileAdGender.unknown
testDevices: <String>[], // Android emulators are considered test devices
);
BannerAd(
adUnitId: adId,
size: AdSize.fullBanner,
targetingInfo: targetingInfo,
listener: (MobileAdEvent event) {
print("BannerAd event is $event");
},
);
}
showBanner() {
myBanner
// typically this happens well before the ad is shown
..load()
..show(
// Positions the banner ad 60 pixels from the bottom of the screen
anchorOffset: 0.0,
// Positions the banner ad 10 pixels from the center of the screen to the right
horizontalCenterOffset: 0.0,
// Banner Position
// anchorType: AnchorType.bottom,
);
}
#override
Widget build(
BuildContext context,
) {
return SafeArea(
child: Scaffold(
backgroundColor: Colors.teal[50],
body: StreamBuilder(
stream: Firestore.instance.collection('car').snapshots(),
builder: (context, snapshot) {
if (snapshot.data == null)
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.red,
valueColor: new AlwaysStoppedAnimation<Color>(Colors.teal),
),
);
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) => SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(8.0),
child: Container(
child: Card(
//shadowColor: Colors.yellow,
child: Column(
children: <Widget>[
//AD
showBanner(),
// Name Container
Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.teal[300],
borderRadius: BorderRadius.only(
topRight: Radius.circular(15),
topLeft: Radius.circular(15))),
width: 400,
height: 50,
child: Text(
snapshot.data.documents[index]['item Name'],
style: TextStyle(
fontFamily: 'Baloo',
fontSize: 25,
color: Colors.white,
),
),
),
//item Image///////////////////////////////////////////////////////////////////////////////
i deleted rest of the code cuz it is very long and allowed in here.
That's happening because you didn't initialize your banner. I changed your showBanner function and added a new function to initialize it:
BannerAd createBannerAd() {
return BannerAd(
adUnitId: BannerAd.testAdUnitId,
size: AdSize.banner,
targetingInfo: targetingInfo,
listener: (MobileAdEvent event) {
print("BannerAd event $event");
},
);
}
showBanner() {
_bannerAd ??= createBannerAd();
_bannerAd
..load()
..show(
// Positions the banner ad 60 pixels from the bottom of the screen
anchorOffset: 0.0,
// Positions the banner ad 10 pixels from the center of the screen to the right
horizontalCenterOffset: 0.0,
// Banner Position
// anchorType: AnchorType.bottom,
);
}
You can also do this inside the initState method if you want:
#override
void initState() {
super.initState();
FirebaseAdMob.instance.initialize(appId: "YOUR-APP-ID");
_bannerAd = createBannerAd()..load();
}
So I am trying to refactor my listView logic. Basically my ListView has become cumbersome with the UI logic , so I decided, why not move certain parts of the UI logic to another class
This is my code
ListPage.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:sample_flutter_works/ListTextArea.dart';
import 'package:sample_flutter_works/Model.dart';
import 'dart:convert';
import 'package:sample_flutter_works/RefreshTableContainer.dart';
class ListPage extends StatefulWidget {
#override
MyListPage createState() => MyListPage();
}
class MyListPage extends State<ListPage> {
MessageList messageList;
List<int> viewTimeInfo;
ScrollController _controller;
_scrollListener() {
}
#override
void initState() {
super.initState();
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
_controller = ScrollController();
_controller.addListener(_scrollListener);
loadMessages(
completionBlock: (dataSet) => {
setState(() {
messageList = dataSet;
})
});
}
void loadMessages({completionBlock}) async {
var jsonString = await rootBundle.loadString('assets/Chat.json');
final jsonResponse = json.decode(jsonString);
if (jsonResponse != null) {
completionBlock(MessageList.fromJSON(jsonResponse));
} else {
completionBlock(null);
}
}
Widget listLayout() {
return ListView.separated(
padding: const EdgeInsets.all(8.0),
itemCount: (messageList != null && messageList.msgList != null)
? messageList.msgList.length
: 0,
separatorBuilder: (context, index) => Divider(
color: Colors.black,
height: 4.0,
),
itemBuilder: (BuildContext context, int index) {
var msgValToSend =
(messageList != null && messageList.msgList != null)
? messageList.msgList[index]
: null;
return Stack(
children: <Widget>[
IntrinsicHeight(
child: Row(
children: <Widget>[
getTheImageLayout(msgValToSend),
new ListTextArea(
msg: msgValToSend,
didTapOnTextArea: tappedOnTextArea,
visibilityCheck: checkForVisibility)
],
),
)
],
);
});
}
tappedOnTextArea(Message msg) {
var viewedInfo = this.viewTimeInfo;
if (viewedInfo != null) {
var indexOfTappedElement = viewedInfo.indexOf(msg.messageID);
if (indexOfTappedElement != null && indexOfTappedElement != -1) {
viewedInfo.removeAt(indexOfTappedElement);
} else {
viewedInfo.add(msg.messageID);
}
} else {
viewedInfo = [msg.messageID];
}
setState(() {
viewTimeInfo = viewedInfo;
});
}
checkForVisibility(bool _visible, Message msg) {
if (msg != null && this.viewTimeInfo != null) {
var checkForIndex = this.viewTimeInfo.indexOf(msg.messageID);
if (checkForIndex != null && checkForIndex != -1) {
_visible = true;
}
}
}
Widget getTheImageLayout(Message msg) {
return Expanded(
flex: 2,
child: Align(
alignment: Alignment.topLeft,
child: Padding(
padding: EdgeInsets.fromLTRB(5, 2.5, 0, 0),
child: Container(
color: Colors.red,
height: 50,
child: Row(
children: <Widget>[
userImageView(msg),
],
)),
)));
}
Widget userImageView(Message msg) {
return Expanded(
flex: 8,
child: Align(
alignment: Alignment.centerLeft,
child: Container(
width: 40.0,
height: 40.0,
decoration:
BoxDecoration(shape: BoxShape.circle, color: Colors.green),
child: ClipOval(
child: Image.network(
(msg.msgUser.userPicUrl != null)
? msg.msgUser.userPicUrl
: 'https://picsum.photos/250?image=9',
fit: BoxFit.fill,
),
))));
}
Future<void> refreshTheChatTable() async {
print(" This is where the logic of pulll 2 refresh must be written ");
loadMessages(
completionBlock: (dataSet) => {
setState(() {
messageList = dataSet;
})
});
}
#override
Widget build(BuildContext context) {
return new RefreshTableContainer(
listLayout: listLayout(),
pull2RefreshAction: refreshTheChatTable,
);
}
}
ListTextArea.dart
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:sample_flutter_works/Model.dart';
class ListTextArea extends StatelessWidget {
Message msg;
Function didTapOnTextArea;
Function visibilityCheck;
ListTextArea({
this.msg,
this.didTapOnTextArea,
this.visibilityCheck
});
#override
Widget build(BuildContext context) {
return Expanded(
flex: 8,
child: GestureDetector(
onTap: didTapOnTextArea(msg),
child: Padding(
padding: EdgeInsets.fromLTRB(0, 2.5, 10, 0),
child: Column(
children: getChildWidgetArray(msg) ,
),
),
));
}
List<Widget> getChildWidgetArray(Message msg) {
var elementalArray = [
Align(
alignment: Alignment.topLeft,
child: Text(
(msg != null) ? msg.msgContent.content : "Data Loading",
style: TextStyle(
background: Paint()..color = Colors.orange,
),
),
),
Spacer(), // Defaults to a flex of one.
Align(
alignment: Alignment.bottomRight,
child: Text(
'Date of sending',
textDirection: TextDirection.rtl,
style: TextStyle(
background: Paint()..color = Colors.blue,
),
),
)
];
var _visible = false;
visibilityCheck(_visible,msg);
var timeInfo = AnimatedOpacity (
opacity: _visible ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child: Align(
child: _visible ? (Align(alignment: Alignment.topLeft,child:Column(children: <Widget>[Text("Last Read :" + (msg.msgTimeInfo.lastReadInfo)),
Text("Delievered :" + (msg.msgTimeInfo.deliveredInfo))],))): null));
elementalArray.add(timeInfo);
return elementalArray;
}
}
The error is as follows:
What I am trying to do ( or had done earlier on when the entire code was in ListPage.dart ) was dynamically calculated cells in a listView, each cell responding to a tap action that shows in more data. I don't understand what I did wrong here at all.
I called the setState in init but inside a callback function. The statelesswidget ListTextArea will not handle the state at all, but returns the tapAction to the StateFulWidget ListPage.dart.
So why am I getting this error. Any insights would be helpful.
In my case, the error occurred when I was setting the state before build was complete, so, I deferred it to the next tick and it worked.
previously
myFunction()
New
Future.delayed(Duration.zero, () async {
myFunction();
});
The problem is in ListTextArea.dart, line
onTap: didTapOnTextArea(msg),
You are calling function didTapOnTextArea in build method instead of passing it as tap listener. You have to replace it with
onTap: (){
didTapOnTextArea(msg);
},