What the title says. I have a freezed constructor tear-off that I'm trying to pass to a Widget and it's not returning null, and I'm trying to figure out what I'm doing wrong. Here is the freezed class:
#freezed
class PatientField with SetFieldOption, _$PatientField {
factory PatientField.firstName(String value) = PatientFirstName;
}
This is a simplified version of my initial Widget (which actually displays properly):
class SinglePatientView2 extends ConsumerWidget {
const SinglePatientView2({Key? key}) : super(key: key);
#override
Widget build(BuildContext context, WidgetRef ref) {
print(PatientField.firstName('something'));
return Theme(
data: Theme.of(context).copyWith(dividerColor: Colors.transparent),
child: Expanded(
child: DefaultTabController(
length: 1,
child: Scaffold(
body: TabBarView(children: [
PersonTabContent<PatientField>(
PatientField.firstName,
)
])))));
}
}
Then Widget that's called above looks like (simplifed version anyway):
class PersonTabContent<T> extends StatelessWidget {
const PersonTabContent(
this.firstNameField, {
Key? key,
}) : super(key: key);
final T Function(String) firstNameField;
#override
Widget build(BuildContext context) {
print(firstNameField('something'));
return Padding(
padding: EdgeInsets.all(doubleBySize(context, 32)),
);
}
}
Looking at the output, the above prints:
PatientField.firstName(value: something)
null
I don't understand why I'm getting a null value in the second Widget's build method. Any insights as to what I'm doing wrong?
As usual, it was my fault. For anyone stumbling onto this, the problem was my version. Constructor tear-offs have only been recently implemented, and I was still specifying dart 2.15.0 in my pubspec.yaml file. For anyone else running into this issue, check your pubspec.yaml file and ensure the top looks like the following:
name: a_new_flutter_project
description: A new Flutter project.
publish_to: 'none'
version: 1.0.0+1
environment:
sdk: ">=2.16.0 <3.0.0"
And that should be that.
Related
2 variables mediumScreen and smallScreen must required
But I wanna option.
FAIL
Do this:
...
Widget? mediumScreen; //Add a question mark after the data type
in your pubspec.yaml file, change eenvironment sdk to:
sdk: ">=2.11.0 <3.0.0"
Check this :
class MyApp extends StatelessWidget {
MyApp(this.yourData);
final int yourData;
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'App Title',
theme: new ThemeData(
primarySwatch: Colors.green,
),
home: new TestClass(yourData),
);
}
}
Another Class:
class TestClass extends StatelessWidget {
TestClass(this.yourData);
final int yourData;
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Padding(
padding: const EdgeInsets.only(left: 20.0, right: 20.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
],
),
),
);
}
}
Because Flutter using null-safety your parameters must declare if it will be null or not.
as in dart documentation
With sound null safety variables are ‘non-nullable’ by default: They can be assigned only values of the declared type (e.g. int i=42), and never be assigned null. You can specify that a type of a variable is nullable (e.g. int? i), and only then can they contain either a null or a value of the defined type.
then in your case try giving it a default value:
class ResponsiveWidget extends StatelessWidget {
final Widget largeScreen;
final Widget mediumScreen;
final Widget smallScreen;
const ResponsiveWidget({
Key? key,
required this.largeScreen,
this.mediumScreen = const SizedBox(), // changed
this.smallScreen = const SizedBox(), // changed
}) : super(key: key);
...
or you can say this will be null like:
class ResponsiveWidget extends StatelessWidget {
final Widget largeScreen;
final Widget? mediumScreen; // changed
final Widget? smallScreen; // changed
const ResponsiveWidget({
Key? key,
required this.largeScreen,
this.mediumScreen,
this.smallScreen,
}) : super(key: key);
...
Code A:
class MyContainer extends StatelessWidget {
const MyContainer({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
// No class type
final container = Container(
width: 100,
height: 100,
color: Colors.red,
);
return container;
}
}
Code B:
class MyContainer extends StatelessWidget {
const MyContainer({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
// Add class type
final Widget container = Container(
width: 100,
height: 100,
color: Colors.red,
);
return container;
}
}
I prefer Code A, but I saw some flutter source code in AppBar:
final Widget toolbar = NavigationToolbar(
leading: leading,
middle: title,
trailing: actions,
centerMiddle: widget._getEffectiveCenterTitle(theme),
middleSpacing: widget.titleSpacing,
);
It's like Code B, why flutter official add class type? For performance? For more readable? or other reasons?
Should I add class type when I use final with local widget?
Dart is type-safe and you decide to add a type or not. Type annotations are optional, because Dart VM performs runtime checks and to ensure variable’s value always matches the variable’s static type. See this for more on Dart Type System.
Can anyone explain why I am getting this error and how to fix it?
I was using my _page() function to create my homepage, but now that I have created a new file for MyHomePage, it is giving me that type error. If I remove that "as Container" part of my context.read line, I get a different error.
I am a bit confused on the "as Container" part anyway, but I think it has to do with flutter 2.0, as it wasn't there until after I migrated to 2.0.
When looking into this error, I saw that I should try to restart the application, so I did that and it seemed to work, until I switch to a different page, and then try switching back to the home page, which is when I get the error.
I am not sure if this is important for this error, but I am using Riverpod for my state management.
my_drawer.dart:
class MyDrawer extends StatefulWidget {
MyDrawer({Key? key}) : super(key: key);
#override
_MyDrawerState createState() => _MyDrawerState();
}
class _MyDrawerState extends State<MyDrawer> {
Widget _homePage = MyHomePage();
Widget _galleryPage = _page(Colors.white, "Gallery");
Widget _bookAppPage = _page(Colors.white, "Book An Appointment");
Widget _aboutUsPage = _page(Colors.white, "About Us");
Widget _contactUsPage = _page(Colors.white, "Contact Us");
Widget _settingsPage = _page(Colors.white, "Settings");
#override
void initState() {
super.initState();
WidgetsBinding.instance!.addPostFrameCallback((_) {
context.read(fragmentProvider).state = _homePage as Container;
});
}
...
...
...
// Temp function to create the rest of the pages
_page(Color color, String title) {
return Container(
height: double.infinity,
width: double.infinity,
color: color,
child: Center(
child: Text(
'$title',
style: TextStyle(fontSize: 30, color: Colors.black),
),
),
);
}
Here is MyHomePage class:
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppBar(),
drawer: MyDrawer(),
body: Stack(
children: [
Text("Home"),
Consumer(
builder: (context, watch, _) {
final body = watch(fragmentProvider).state;
return body;
},
),
],
),
);
}
}
You're trying to cast _homePage as a Container, but _homePage is an instance of MyHomePage which is a StatefulWidget. All your other pages are actually wrapped in Container, so those casts will succeed.
I'm not sure what the type of context.read(fragmentProvider).state is, so it's hard to say what's the actual fix here without more information.
From this code:
context.read(fragmentProvider).state = _homePage as Container;
Replace the Container with Widget:
context.read(fragmentProvider).state = _homePage as Widget;
I have refactored my code in order to get the result that I was looking for. Instead of using Riverpod and trying to use state management to switch between pages, I opted for the built in Navigator and MaterialPageRoute widgets to navigate between pages in my drawer.
I replaced this code for each context.read() line.
Navigator.push(context,
MaterialPageRoute(builder: (context) => new MyHomePage()));
I removed the Consumer widget as well as all of the Widget _...Page = ...; lines. I also got rid of my _page() function and just went ahead and created basic pages for each page and then imported them into my_drawer.dart.
As Alex Hartford was saying in the comments, I was just over complicating everything by trying to use Riverpod to switch between each page.
After making these changes, the app behaves exactly as I need it to. Thanks to everyone who replied and helped me out. I really appreciate it!
I have this widget tree
return Container(
color: Colors.blue,
child: Container(
child: Text("Child"),
),
);
Is there a way to remove a parent widget from the tree or conditionally include it?
For example if a state variable such as includeBlueContainer was false, I would like to not render the blue container (but show everything else).
I couldn't achieve an optionally include reusable widget but I've been using this pattern which does achieve what I wanted to achieve. I haven't given this a great amount of thought but I still feel there is a better solution somewhere.
class MyContainer extends StatelessWidget {
final Widget child;
final bool isIncluded;
MyContainer({this.child, this.isIncluded = true});
Widget build(BuildContext context) {
if (!isIncluded) return child;
return Container(
color: Colors.blue,
child: child,
);
}
}
Edit: I made a package out of this: https://pub.dev/packages/conditional_parent_widget
Which you can use like:
import 'package:flutter/widgets.dart';
import 'package:conditional_parent_widget/conditional_parent_widget.dart';
// ...
return ConditionalParentWidget(
condition: includeBlueContainer,
child: Text("Child"),
parentBuilder: (Widget child) => Container(
color: Colors.blue,
child: child,
),
);
Internally it is just:
import 'package:flutter/widgets.dart';
class ConditionalParentWidget extends StatelessWidget {
const ConditionalParentWidget({
Key? key,
required this.condition,
required this.child,
required this.parentBuilder,
}) : super(key: key);
final Widget child;
final bool condition;
final Widget Function(Widget child) parentBuilder;
#override
Widget build(BuildContext context) {
return condition ? this.parentBuilder(this.child) : this.child;
}
}
I have no Idea anymore.
I am using Mobx for really simple State Management.
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:jw_helper/state/globalState.dart';
class Router extends StatelessWidget {
const Router({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
final _globalState = GlobalState();
return Column(
children: <Widget>[
Container(
child: Observer(
builder: (_) => Text(_globalState?.currentIndex?.toString()),
),
),
MaterialButton(
onPressed: () {
_globalState.setCurrentIndex(1);
},
child: Text("Press me"),
),
],
);
}
}
When i mutate the state in this widget the value updates.
When i mutate the same Observable in another Widget the Observer is not rebuilding.
Only the observer in the same Widget where the State is mutated is updated.
My Mobx Code:
import 'package:mobx/mobx.dart';
// Include generated file
part 'globalState.g.dart';
// This is the class used by rest of your codebase
class GlobalState = _GlobalState with _$GlobalState;
// The store-class
abstract class _GlobalState with Store {
#observable
int currentIndex = 0;
#action
void setCurrentIndex(index) {
currentIndex = index;
print(currentIndex);
}
}
Small note: The Print Statement is always fired
Maybe someone knows how to fix this.
Thank you ;)
The Problem was solved with Help from a Discord Mobx Channel Member.
The solution was to wrap the whole App in a provider Widget.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
------------------------------------------------
return Provider<GlobalState>(
create: (context) => GlobalState(),
------------------------------------------------
child: MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SplashScreen(),
),
);
}
}
In the Widgets consuming the Mobx Class i did:
class Router extends StatelessWidget {
const Router({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
final _globalState = Provider.of<GlobalState>(context);
return Column(
children: <Widget>[
Container(.....
Hopefully this helps somebody to get up and running ;)
Not a finished (nor will be) app but hopefully can help as a start.
Flutter + Mobx + (Multi) Providers
https://github.com/jonataswalker/flutter-example
You have two instances of GlobalState class. One for each widget. In order for Observer works correctly it needs to observe always the same instance.
Using "Provider" you are kind of using the Singleton pattern which solves the problem since both variables start refering to the same instance
Had same problem
Always rebuild mobX after any changes u place inside main store file
flutter pub run build_runner build --delete-conflicting-outputs