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);
...
Related
Is it possible to "merge" two widgets together? I want to build an OptionalWrapper component, that wraps a widget by another widget only if a condition is matched.
The usage should look something like this, I provide a child and if the showWrapper condition is true, this child is put as the child of the wrapper widget.
OptionalWrapper(
showWrapper: !isSmallScreen(context),
wrapper: Padding(
padding: const EdgeInsets.only(top: 16.0),
child: // <-- Here should the child be rendered
),
child: Text("Hello"),
),
of course I could do something like this:
!isSmallScreen(context) ? Padding(
padding: const EdgeInsets.all(16.0),
child: Text("Hello"),
) : Text("Hello"),
But here I declare my Text widget twice which I want to avoid.
The implementation would look like this:
import 'package:flutter/material.dart';
class OptionalWrapper extends StatelessWidget {
final bool showWrapper;
final Widget wrapper;
final Widget child;
const OptionalWrapper({
Key? key,
required this.showWrapper,
required this.wrapper,
required this.child,
}) : super(key: key);
#override
Widget build(BuildContext context) {
if (showWrapper) {
// return the wrapper widget but subject child as child of the wrapper widget
} else {
return child;
}
}
}
You can create builder method like
import 'package:flutter/material.dart';
class OptionalWrapper extends StatelessWidget {
final bool showWrapper;
final Widget Function(Widget) wrapper;
final Widget child;
const OptionalWrapper({
Key? key,
required this.showWrapper,
required this.wrapper,
required this.child,
}) : super(key: key);
#override
Widget build(BuildContext context) {
if (showWrapper) {
return wrapper(child);
} else {
return child;
}
}
}
You can make the padding as EdgeInsects.zero if the condition fails
Padding(
padding: !isSmallScreen(context)?const EdgeInsets.all(16.0): EdgeInsects.zero,
child: Text("Hello"),
Get the working code sample here
I have an RxList of addOnProducts which contains product and selected attributes.
I am trying to implement the simple multiSelectable grid View, but on clicking the checkBox the selected attribute changes but it is not reflected back to the ui,
If i refresh it will be updated.
I tried Obx()=> (); widget , It is still not updating
My ProductController
class ProductsController extends GetxController {
late Worker worker;
static ProductsController instance = Get.find();
RxList<ProductModel> products = RxList<ProductModel>([]);
RxList<CheckProduct> addOnProducts = <CheckProduct>[].obs;
String collection = "products";
#override
void onReady() {
super.onReady();
products.bindStream(getAllProducts());
worker = once(products, (List<ProductModel> value) {
fillAddOnProducts(value);
}, condition: () => products.isNotEmpty);
}
Stream<List<ProductModel>> getAllProducts() => FirebaseFirestore.instance
.collection(collection)
.snapshots()
.map((query) => query.docs
.map((item) => ProductModel.fromMap(item.data(), item.id))
.toList());
void fillAddOnProducts(List<ProductModel> products) => {
products.forEach((element) {
addOnProducts.add(CheckProduct(product: element, selected: false));
})
};
}
class CheckProduct {
ProductModel product;
bool selected;
CheckProduct(
{required ProductModel this.product, required bool this.selected});
}
My Grid View
class AddOns extends StatelessWidget {
const AddOns({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [],
title: Text("Select Addons"),
),
body: Obx(() => GridView.count(
crossAxisCount: 2,
children: productsController.addOnProducts
.map((element) => ProductWidget(product: element))
.toList(),
)));
}
}
class ProductWidget extends StatelessWidget {
final CheckProduct product;
const ProductWidget({Key? key, required this.product}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
color: Colors.yellow,
margin: EdgeInsets.all(10),
child: Stack(
alignment: Alignment.center,
children: [
Positioned(
top: 4,
left: 4,
child: Checkbox(
value: product.selected,
onChanged: (value) {
print("value of the value is : $value");
print("value of product selected before is: " +
product.selected.toString());
product.selected = value!;
print("value of product selected after is: " +
product.selected.toString());
},
),
),
],
));
}
}
Therefore in the console it is :
I/flutter (20067): value of the value is : true
I/flutter (20067): value of product selected before is: false
I/flutter (20067): value of product selected after is: true
But the checkBox is not updating, it updates only when i refresh, How to overCome this? Adding Obx() to the parent isn't helping..
Find the github link to code below here which has just the question and and the problem faced..
After going through your code. I've implemented the following that will change state without hot reload:
In your main dart you do not need to put your product controller here as you are not using it
main.dart
import 'package:flutter/material.dart';
import 'grid.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: GridSelect(),
);
}
}
Next, I have changed your grid class to generate a list of product widget as the size of the addProduct list length. In my opinion this is a better way to write GridView counts children. Remove obx from your gridview and change your stateful widget to stateless as you are using Getx. It will manage your state even in a stateless widget. Add your product controller here as you will access addProduct list from the controller class.
grid.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:test_project/controllers/productController.dart';
import 'package:test_project/productWidget.dart';
class GridSelect extends StatelessWidget {
final _controller = Get.put(ProductController());
GridSelect({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: GridView.count(
crossAxisCount: 2,
children: List.generate(_controller.addOnProducts.length, (index) => ProductWidget(index: index))
),
);
}
}
In your product controller class, remove the instance as it is not important. That is the only change here:
ProductController.dart
import 'package:get/get.dart';
import 'package:test_project/models/productModel.dart';
class ProductController extends GetxController {
RxList<CheckProduct> addOnProducts = <CheckProduct>[].obs;
#override
void onReady() {
super.onReady();
addOnProducts.add(CheckProduct(product: ProductModel('productOne', 20)));
addOnProducts.add(CheckProduct(product: ProductModel('productTwo', 25)));
addOnProducts.add(CheckProduct(product: ProductModel('productThree', 30)));
addOnProducts.add(CheckProduct(product: ProductModel('productFour', 40)));
}
}
class CheckProduct {
ProductModel product;
RxBool selected = false.obs;
CheckProduct({
required this.product,
});
}
Lastly, your productWidget class needs a required value index. So, the widget knows which index in gridview the user is clicking and use Obx() here in checkbox as you have an observable value selected here. Remember to always use Obx() when you have an obs value. This will update the widget whenever an obs value changes. Here, if you notice we are using Get.find() instead of Put as Get.put is already inside the scope so all you need to do is find the controller that you will use. You can find or put multiple controllers and update values as much as you want.
productWidget.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:test_project/controllers/productController.dart';
class ProductWidget extends StatelessWidget {
final ProductController _controller = Get.find();
final int index;
ProductWidget({Key? key, required this.index}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
color: Colors.yellow,
margin: EdgeInsets.all(20),
child: Stack(
alignment: Alignment.center,
children: [
Positioned(
top: 4,
left: 4,
child: Obx(()=>Checkbox(
value: _controller.addOnProducts[index].selected.value,
onChanged: (value) {
print("value of the value is : $value");
print("value of product selected before is: " +
_controller.addOnProducts[index].selected.toString());
_controller.addOnProducts[index].selected.value = value!;
print("value of product selected after is: " +
_controller.addOnProducts[index].selected.toString());
},
)),
)
],
),
);
}
}
Go through GetX documentation for proper use of GetX. Even though I have 2 apps in Playstore with GetX, I still go through documentation from time to time. They have a clear documentation on how to manage state.
In ProductWidget adding an additional Obx() solved my problem
class ProductWidget extends StatelessWidget {
final CheckProduct product;
const ProductWidget({Key? key, required this.product}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
color: Colors.yellow,
margin: EdgeInsets.all(10),
child: Stack(
alignment: Alignment.center,
children: [
Positioned(
top: 4,
left: 4,
// Even the child needs Obx() ; The parent's Obx() is not reflected here
child: Obx(()=>(Checkbox(
value: product.selected,
onChanged: (value) {
print("value of the value is : $value");
print("value of product selected before is: " +
product.selected.toString());
product.selected = value!;
print("value of product selected after is: " +
product.selected.toString());
},
),))
),
],
));
}
I would like to create my own theme properties which can be set in runtime dynamically.
I tried to create an extension for the TextTheme which looks like that:
extension CustomTextTheme on TextTheme {
TextStyle get heading => themeMode == ThemeMode.light
? TextStyle(
color: GlobalTheme.defaultLightTheme.textTheme.headline.color,
fontSize: GlobalTheme.defaultLightTheme.textTheme.headline.fontSize,
)
: TextStyle(
color: GlobalTheme.defaultDarkTheme.textTheme.headline.color,
fontSize: GlobalTheme.defaultLightTheme.textTheme.headline.fontSize,
);
}
The question is how I can change the extension properties on runtime dynamically.
What I want to archive with this is, that I can load a "theme config" from the server and set that theme on each device dynamically.
To pass and get values inside the widget tree you need InheritedWidget.
This is a special type of Widgets that just transfer the information between widgets (like Theme delivers ThemeData).
You can not extend ThemeData with new fields as extensions will not trigger updates on Theme.
But you can create your own CustomTheme which will coexist with the original.
class CustomThemeData {
const CustomThemeData(this.heading);
final TextStyle heading;
#override
bool operator ==(Object other) =>
identical(this, other) ||
other is CustomThemeData &&
runtimeType == other.runtimeType &&
heading == other.heading;
#override
int get hashCode => heading.hashCode;
}
Now create an InheritedWidget that wll provide the custom theme data value (via of) and will add a possibility to update the data (via update)
class CustomTheme extends InheritedWidget {
const CustomTheme({
Key? key,
required this.data,
required this.onUpdate,
required Widget child,
}) : super(key: key, child: child);
final CustomThemeData data;
final void Function(CustomThemeData) onUpdate;
static CustomThemeData of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<CustomTheme>()!.data;
}
static void update(BuildContext context, CustomThemeData data) {
context.dependOnInheritedWidgetOfExactType<CustomTheme>()!.onUpdate(data);
}
#override
bool updateShouldNotify(covariant CustomTheme oldWidget) {
return data != oldWidget.data;
}
}
Here is a holder for custom theme
class ThemeSwitcherWidget extends StatefulWidget {
final CustomThemeData initialTheme;
final Widget child;
const ThemeSwitcherWidget({
Key? key,
required this.initialTheme,
required this.child,
}) : super(key: key);
#override
_ThemeSwitcherWidgetState createState() => _ThemeSwitcherWidgetState();
}
class _ThemeSwitcherWidgetState extends State<ThemeSwitcherWidget> {
CustomThemeData? _updatedTheme;
#override
Widget build(BuildContext context) {
return CustomTheme(
data: _updatedTheme ?? widget.initialTheme,
onUpdate: (newData) => setState(() {
_updatedTheme = newData;
}),
child: widget.child,
);
}
}
And here is an example on how to use all this beauty:
void main() {
runApp(
const ThemeSwitcherWidget(
initialTheme: CustomThemeData(TextStyle()),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'text',
style: CustomTheme.of(context).heading,
),
ElevatedButton(
onPressed: () {
CustomTheme.update(
context,
const CustomThemeData(TextStyle(
color: Colors.red,
fontSize: 44,
)));
},
child: const Text('Change theme')),
],
),
),
),
);
}
}
To make the code less verbose you may use provider which will do all that magic with updates for you.
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.
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;
}
}