i get an error that my BannerAd variable is not initialized and my app crash, so i move my _banner in top of code and still not work, i move it inside the state class and still not initialized what should need to do to be initialized ?
class _MyHomePageState extends State<MyHomePage> {
late BannerAd _banner;
#override
void didChangeDependencies(){
super.didChangeDependencies();
final adState = context.read(adStateProvider);
adState.initialization.then((value)
{
setState(() {
_banner = BannerAd(size: AdSize.banner, adUnitId: adState.bannerAdUniteId, listener: adState.adListener, request: AdRequest())..load();
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text('pub'),
if(_banner == null)Text('null hhhhhhhhhhhhhhhhhhhhhhhhhhhhh')
else Expanded(child: AdWidget(ad: _banner),),
That's because your widget builds before _banner is initialized, even if you initialized it in initState, will still not work, because you initialize it after a future completes, and that future completes after the build method got executed. so the solution here is to remove the late keyword before BannerAd and also make it nullable. like this:
// this doesn't give you any initialization errors
// because _banner will have a default value of null
BannerAd? _banner;
now you will have a different problem, because AdWidget takes a non-null object, you need to assert that _banner is not null with null assertion operator !, like this:
AdWidget(ad: _banner!)
this way it's like you're telling the compiler that "I'm sure that _banner is not null", because you only build this widget if _banner is not null because of the if else clause
Note that when using !, you have to be completely sure that the object will actually be not null then, if it's null, it will throw a null assertion exception.
you can read and practice on null safety with this dart codelab
Related
I want to implement Refresh Indicator on my list view. On pulling the list down I'm calling an API to sync the list with server.
Now when I sync the list while I'm on the same page it refreshes the list.
But If I pop the screen by going back and again navigating to the same screen it doesn't refresh the list.
The sync is taking some time and the code for syncing is written in ChangeNotifier.
class ListScreen extends StatefulWidget {
const ListScreen({super.key});
#override
State<ListScreen> createState() => _ListScreenState();
}
class _ListScreenState extends State<ListScreen> {
int itemCount = 1;
SyncViewModel? _syncViewModel;
#override
Widget build(BuildContext context) {
_syncViewModel = context.watch<SyncViewModel>();
return Builder(builder: (context) {
return RefreshIndicator(
displacement: 250,
backgroundColor: Colors.yellow,
color: Colors.red,
strokeWidth: 3,
triggerMode: RefreshIndicatorTriggerMode.onEdge,
onRefresh: () async {
await _syncViewModel!.callSyncAPIs();
setState(() {
itemCount = itemCount + 1;
});
},
child: Scaffold(
backgroundColor: const Color(0xff246df8),
appBar: AppBar(
title: const Text('Refresh Indicator'),
backgroundColor: const Color(0xff246df8),
),
body: _buildCardDesign(),
),
);
});
}
import 'package:flutter/material.dart';
class SyncViewModel extends ChangeNotifier {
bool _loading = false;
bool get loading => _loading;
Future<void> callSyncAPIs() async {
_loading = true;
notifyListeners();
await Future.delayed(const Duration(seconds: 5));
_loading = false;
notifyListeners();
}
}
Earlier I was getting following exception, then I also checked for mounted before setState. But now in my use case the mounted is always false.
Exception has occurred.
FlutterError (setState() called after dispose(): _ListScreenState#5934b(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().)
How can I refresh the list when the sync completes If user navigates to the list page?
How can I show refreshing indicator if user navigates to the list page when sync is in progress?
I'm new to flutter but I have a widget that wraps a custom painter. I am trying to get it to work so I can supply a Widget to this child widget's constructor and then use that widget as the child of the custom painter.
For example:
class MyPainterWrapper extends StatefulWidget {
Widget _childWidget;
SceneRender([this._childWidget]);
#override
State<StatefulWidget> createState() {
return new MyPainterWrapperState(_childWidget);
}
}
class MyPainterWrapperState extends State<SceneRender> {
Widget _childWidget;
MyPainterWrapperState(this._childWidget);
#override
Widget build(BuildContext context) {
return Column(
children: [
CustomPaint(painter: MyPainter(), child: _childWidget)
],
);
}
}
And in another widget (called testWidget):
bool _answerCorrect = false;
bool _answerInputted = false;
var _msgController = TextEditingController();
FocusNode _answerFieldFocus = new FocusNode();
DictionaryEntry _currentEntry;
void _checkIfCorrect(String answerGiven) {
setState(() {
_answerCorrect = false;
if (_currentEntry.Full_Word == answerGiven)
_answerCorrect = true;
else if (_currentEntry.dictReadings.isNotEmpty) {
for (AlternateDictionaryEntryReading entryReading in _currentEntry
.dictReadings) {
if (entryReading.Alternate_Reading == answerGiven) {
_answerCorrect = true;
break;
}
}
}
_answerInputted = true;
_msgController.clear();
});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('test'),
),
body: MyPainterWrapper(Center(Container(Column(children: <Widget>[
if (_answerCorrect && _answerInputted) Text('CORRECT!'),
if (!_answerCorrect && _answerInputted) Text('WRONG:'),
if (_answerInputted)
Text(_currentEntry.Full_Word),
if (_answerInputted)
for(AlternateDictionaryEntryReading reading in _currentEntry.dictReadings)
Text(reading.Alternate_Reading),
Container(
constraints: BoxConstraints.expand(
height: 100,
width: 1000
),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
for (DictionaryTranslation translation in _currentEntry.dictTranslations)
Text(translation.Translation),
],
)
),
),
Text('Enter Answer:',),
TextField(
controller: _msgController,
focusNode: _answerFieldFocus,
onSubmitted: (String value) {
_checkIfCorrect(value);
_answerFieldFocus.requestFocus();
},
)
This works to render the first time correctly, but any setState calls from checkIfCorrect from testWidget do not force the child widget to rebuild. I've tried testing it this way and it works, so that leads me to believe that I'm passing the widget incorrectly to have it redrawn via setState
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('test'),
),
body: CustomPaint(painter: TestPainter(), child: Center(
child: Container(...))
Your MyPainterWrapperState class reads like you are creating a new _childWidget property inside your state (which has a default value of null). You are then using it to initialize a new instance of MyPainterWrapperState, then throwing away the instance of MyPainterWrapper() that you just created.
You're not actually using the stateful part of your stateful widget at all; You're just calling a method that returns something once.
That said, your approach is basically right, but the implementation is off a little.
My advice:
First, you can use named properties to supply constructor arguments to your class. I've made that change in the code snippet shown below.
The State class of a stateful widget supplies a widget property that should be used to reference the properties of the widget that created it. The State widget should also have a solitary initializer that accepts no arguments.
Also good to know is that the State class provides an initState() method that you can override to initialize any class local variables that you declare. This should be used to give a value to your _childWidget property.
Finally, anything you expect to be rebuilt should be inside the MyPainterWrapperState() class. Your SceneRender() method doesn't appear in your code, but you might want to move it into MyPainterWrapperState() if you expect the scene to change based on the value of the child.
I suggest these changes.
Pass arguments to MyPainterWrapper via named arguments.
Remove the argument to MyPainterWrapperState() and reference the child through the widget property supplied to the state.
Initialize _childWidget by overriding initState()
If SceneRender() does anything that depends on the value of _childWidget, move it to the build() method of MyPainterWrapperState().
The Flutter docs are great, and the Flutter team has created a ton of helpful YouTube videos that explain in a couple of minutes examples of how to use dozens of them. For a better understanding of StatefulWidget, you can read about it here.
If you make these changes, your solution would look something like the code snippet below.
Presuming you make those changes, you would alter your call to MyPainterWrapper() to use named properties.
Change this line
body: MyPainterWrapper(Center(Container(Column(children: <Widget>[
To this
body: MyPainterWrapper(child: Center(Container(Column(children: <Widget>[
This won't get you to done, but it will get you closer. I also haven't run this through a compiler, so there are probably errors in the snippet, but this should serve to illustrate the approach.
class MyPainterWrapper extends StatefulWidget {
MyPainterWrapper(
{
#required child: this.childWidget,
}
);
final Widget childWidget;
// Not sure what this does, but I'm pretty sure that it doesn't
// provide anything into the widget tree.
// If it mutates its arguments, then you might still need it.
// SceneRender([this._childWidget]);
#override
State<StatefulWidget> createState() {
// Note no arguments.
return new MyPainterWrapperState();
}
}
class MyPainterWrapperState extends State<MyPainterWrapper> {
// This is an uninitialized variable inside this class.
Widget _childWidget;
// MyPainterWrapperState(this._childWidget);
// Initialize state variables here.
#override initState() {
// Assigns the widget class initializer to your state variable.
_childWidget = widget.childWidget;
}
#override
Widget build(BuildContext context) {
return Column(
children: [
CustomPaint(painter: MyPainter(), child: _childWidget)
],
);
}
}```
Im trying to implement syncfusion and display maps with flutter. And I seem to have run into a nullcheck problem right from start (actually the tutorial video). Its the [MapShapeLayer(source: _shapeSource)], that wont work it says:
"The argument type 'MapShapeSource?' can't be assigned to the parameter type 'MapShapeSource'."
As you can see I have a ? after the MapShapeSource but how I try to fix this it doesn't seem to work, any idées?
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
MapShapeSource? _shapeSource;
#override
void initState() {
_shapeSource = MapShapeSource.network(
'http://www.json-generator.com/api/json/get/bVqXoJvfjC?indent=2');
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.fromLTRB(10, 50, 0, 0),
child: SfMaps(
layers: [MapShapeLayer(source: _shapeSource)],
),
),
);
}
}
You had used a nullable field for a non-nullable MapShapeLayer.source property. You should use a non-nullable field for that. Please check this documentation to know about loading JSON from the network.
The error
The message indicates that a nullable _shapeSource is being used somewhere in your code. While adding a late modifier is a promise to the compiler that you will be responsible and initialize _shapeSource to a non-nullable value before you use it. The Dart team also recommends that you use good programming practice to ensure that you handle the nullable case before using the value. In this case, what happens if you have no access to the network (would _shapeSource crash the app?)?
Here's a couple of examples:
In the case below the nullable argument dairy is checked for null before it is used with an alternative.
// Using null safety:
makeCoffee(String coffee, [String? dairy]) {
if (dairy != null) {
print('$coffee with $dairy');
} else {
print('Black $coffee');
}
}
In the next case the nullable value is cast as a non-nullable String before being used.
// Using null safety:
requireStringNotObject(String definitelyString) {
print(definitelyString.length);
}
main() {
Object maybeString = 'it is';
requireStringNotObject(maybeString as String);
}
Suggestions
In your code you might create an alternative widget to display in the event of network failure or, try casting the value, or provide a null check before using the value with a ternary operator (see example below). This would be a better practice than just adding the late modifier by itself.
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.fromLTRB(10, 50, 0, 0),
child: SfMaps(
layers: [MapShapeLayer(source: (_shapeSource != null) ? _shapeSource : _defaultNonNullSource)],
),
),
);
}
You can read up some more on null safety at the dart.dev website and you can practice what works with your code and get a look at suggestions by pasting the code into dartpad.dev with null safety enabled.
I'm trying to display an image based on (base64) data coming from a backend, but I keep getting the error bytes != null': is not true.
Here's my code:
class _FuncState extends State<Func> {
Uint8List userIconData;
void initState() {
super.initState();
updateUI();
}
void updateUI() async {
await getUserIconData(1, 2, 3).then((value) => userIconData = value);
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
Container(
child: CircleAvatar(
backgroundImage: Image.memory(userIconData).image, // <--- problem here
maxRadius: 20,
),
),
),
);
}
}
Helper code:
Future<Uint8List> getUserIconData(
role,
id,
session,
) async {
var url = Uri.https(kMobileAppAPIURL, kMobileAppAPIFolder);
var response = await http.post(url, body: {
'method': 'getUserProfilePic',
'a': id.toString(),
'b': role.toString(),
'c': session.toString(),
});
if (response.statusCode == 200) {
Map data = jsonDecode(response.body);
return base64Decode(data['img']);
}
return null;
}
I have stepped through the code with a debugger and I have confirmed the helper function is returning the correct series of bytes for the image.
I'd appreciate any pointers.
Further note. The error also says:
Either the assertion indicates an error in the framework itself, or we
should provide substantially more information in this error message to
help you determine and fix the underlying cause. In either case,
please report this assertion by filing a bug on GitHub
This is quite simple; if you take a look at your code you should be able to follow through this sequence of operations.
The widget is created. No action. At this point userIconData is null.
initState is called. async http call is initiated. userIconData == null
build is called. build occurs, throws error. userIconData == null
http call returns. userIconData is set. userIconData == your image
Due to not calling setState, your build function won't run again. If you did, this would happen (but you'd still have had the exception earlier).
build is called. userIconData is set. userIconData == your image
The key here is understanding that asynchronous calls (anything that returns a future and optionally uses async and await) do not return immediately, but rather at some later point, and that you can't rely on them having set what you need in the meantime. If you had previously tried doing this with an image loaded from disk and it worked, that's only because flutter does some tricks that are only possible because loading from disk is synchronous.
Here are two options for how you can write your code instead.
class _FuncState extends State<Func> {
Uint8List? userIconData;
// if you're using any data from the `func` widget, use this instead
// of initState in case the widget changes.
// You could also check the old vs new and if there has been no change
// that would need a reload, not do the reload.
#override
void didUpdateWidget(Func oldWidget) {
super.didUpdateWidget(oldWidget);
updateUI();
}
void updateUI() async {
await getUserIconData(widget.role, widget.id, widget.session).then((value){
// this ensures that a rebuild happens
setState(() => userIconData = value);
});
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Container(
// this only uses your circle avatar if the image is loaded, otherwise
// show a loading indicator.
child: userIconData != null ? CircleAvatar(
backgroundImage: Image.memory(userIconData!).image,
maxRadius: 20,
) : CircularProgressIndicator(),
),
),
);
}
}
Another way to do the same thing is to use a FutureBuilder.
class _FuncState extends State<Func> {
// using late isn't entirely safe, but we can trust
// flutter to always call didUpdateWidget before
// build so this will work.
late Future<Uint8List> userIconDataFuture;
#override
void didUpdateWidget(Func oldWidget) {
super.didUpdateWidget(oldWidget);
userIconDataFuture =
getUserIconData(widget.role, widget.id, widget.session);
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Container(
child: FutureBuilder(
future: userIconDataFuture,
builder: (BuildContext context, AsyncSnapshot<Uint8List> snapshot) {
if (snapshot.hasData) {
return CircleAvatar(
backgroundImage: Image.memory(snapshot.data!).image,
maxRadius: 20);
} else {
return CircularProgressIndicator();
}
},
),
),
),
);
}
}
Note that the loading indicator is just one option; I'd actually recommend having a hard-coded default for your avatar (i.e. a grey 'user' image) that gets switched out when the image is loaded.
Note that I've used null-safe code here as that will make this answer have better longevity, but to switch back to non-null-safe code you can just remove the extraneous ?, ! and late in the code.
The error message is pretty clear to me. userIconData is null when you pass it to the Image.memory constructor.
Either use FutureBuilder or a condition to check if userIconData is null before rendering image, and manually show a loading indicator if it is, or something along these lines. Also you'd need to actually set the state to trigger a re-render. I'd go with the former, though.
I'm trying to implement Provider instead of using State so after reading some tutorials, that are little hard to me to understand, because I didn't find something clear to say: "You're going to use Firebase, then here you should use ChangeNotifierProvider or Provider or StreamProvider etc."
So I found a tutorial by Jeff Delaney at Fireship that I tried to apply to my use case, but despite it works I'm not sure I did it properly, mostly because I get this message saying "The getter was called on null". I can suppress it by putting an "?" like user?.name but I want to understand what is going on and why despite the error the code still works fine.
Here's the code I have:
First I have a normal User model that works just fine creating model and returning name, tlf and other stuff from Firebase. This part is ok.
Then I have this database service from the Jeff's code:
class DatabaseService {
final Firestore _db = Firestore.instance;
/// Get a stream of a single document
Stream<User> streamUser (String id) {
return _db
.collection('profiles')
.document(id)
.snapshots()
.map((snap) => User.fromMap(snap.data));
}
}
Then I have the screen where I implemented the Provider:
class GestorScreen extends StatelessWidget {
final db = DatabaseService();
final FirebaseUser firebaseUser;
GestorScreen({Key key, #required this.firebaseUser});
#override
Widget build(BuildContext context) {
return StreamProvider<User>.value(
value: db.streamUser(firebaseUser.uid),
child: DashboardButtons(),
);
}
}
And the DashboardButtons class:
class DashboardButtons extends StatelessWidget {
#override
Widget build(BuildContext context) {
var user = Provider.of<User>(context);
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
user?.updated != null ? _button(CustomColors.emerald, () {
Navigator.push(context,
MaterialPageRoute(builder: (context) =>
Dashboard(user: user)));
}, 'Gestionar Perfil') : _button(CustomColors.emerald, () {
Navigator.push(context,
MaterialPageRoute(builder: (context) =>
UserAlta(user: user,)));
}, ' Dar de Alta (ßeta)'),
SizedBox(height: SizeConfig.blockSizeHorizontal * 7,),
.................
.................
.................
],
)
),
backgroundColor: CustomColors.newCreme,
)
}
}
The code works fine but if delete the "?" from user.updated it says that The getter 'updated' was called on null... still it works fine.
Can someone help me with this? Where is the problem with the getter? And did I implemented the Provider the right way?
Get the error when the user is null.
user?.updated is equal to user == null ? null : user.updated
its a safe navigation operator, when the user object is null, the whole expression returns null. Otherwise updated() method is run.
safe navigation operator