Getting error when retrieving device screen info in Flutter - flutter

I have defined a class to retrieve the device info such as the width, height, orientation, and so on.
import 'package:flutter/material.dart';
class Device {
static double? height;
static double? width;
static String? orientationType;
static String? deviceType;
void init(BuildContext context) {
final mediaQueryData = MediaQuery.of(context);
final appBar = AppBar();
final appBarHeight = appBar.preferredSize.height;
final statusBarHeight = mediaQueryData.padding.top;
final bottomBarHeight = mediaQueryData.padding.bottom;
height = mediaQueryData.size.height - appBarHeight - statusBarHeight - bottomBarHeight;
width = mediaQueryData.size.width;
orientationType = (mediaQueryData.orientation == Orientation.portrait) ? "Portrait" : "Landscape";
deviceType = (orientationType == "Portrait" && width! < 600) ? "Mobile" : "Tablet";
}
}
When I try to calculate the size then as shown below I'm getting an error.
return Container(
width: Device.width! * 0.9,
);
Error message:
Null check operator used on a null value
But when I use it without multiplying width: Device.width it works fine and need not have to insert the null check operator as well.

Converting it to singleton class with parameters.
import 'package:flutter/material.dart';
class Device {
late double height;
late double width;
late String orientationType;
late String deviceType;
static final Device _inst = Device._internal();
Device._internal();
factory Device({BuildContext context}) {
final mediaQueryData = MediaQuery.of(context);
final appBar = AppBar();
final appBarHeight = appBar.preferredSize.height;
final statusBarHeight = mediaQueryData.padding.top;
final bottomBarHeight = mediaQueryData.padding.bottom;
_inst.height = mediaQueryData.size.height - appBarHeight - statusBarHeight - bottomBarHeight;
_inst.width = mediaQueryData.size.width;
_inst.orientationType = (mediaQueryData.orientation == Orientation.portrait) ? "Portrait" : "Landscape";
_inst.deviceType = (orientationType == "Portrait" && width! < 600) ? "Mobile" : "Tablet";
return _inst;
}
}
Use it like this:
Device(context: context).height
For more info on this topic visit:
How do you build a Singleton in Dart?
In Dart, is it possible to pass an argument in a singleton?

Related

From flame 0.29.0 to flame 1.0.0 and from box2d_flame: ^0.4.6 to flame_forge2d 0.11.0

I'm Migrating to flame 1.1.1 from flame v0.29.4 and I can't find a good roadmap.
How can I effectively replace Box2DComponent ? Attributes like components and viewport for example, i can't understand where and how to replace them.
Is correct replacing BaseGame with Flame game ? And would it be correct replacing Box2DComponent with Forge2DGame ?
under here my 3 classes. I knwo it's a difficult question but I could really use some help.
Thank you
import 'dart:math' as math;
import 'dart:ui' as ui;
import 'package:artista_app/features/tastes/model/presentation/tastes_vm.dart';
import 'package:box2d_flame/box2d.dart';
import 'package:flame/box2d/box2d_component.dart';
import 'package:flame/box2d/viewport.dart' as box2d_viewport;
import 'package:flame/components/mixins/tapable.dart';
import 'package:flame/game/base_game.dart';
import 'package:flame/gestures.dart';
import 'package:flame/text_config.dart';
import 'package:flutter/material.dart';
class BubblePicker extends BaseGame with TapDetector {
PickerWorld _pickerWorld;
#override
ui.Color backgroundColor() {
return Colors.transparent;
}
final void Function(TastesVM) onTastesChange;
BubblePicker(TastesVM tastes, {this.onTastesChange}) : super() {
_pickerWorld = PickerWorld(tastes);
_pickerWorld.initializeWorld();
onTastesChange?.call(TastesVM(
tastes: (tastes.tastes.where((taste) => taste.checked).toList())));
}
#override
void onTapUp(TapUpDetails details) {
_pickerWorld.handleTap(details);
onTastesChange?.call(TastesVM(tastes: _pickerWorld.checkedTastes));
super.onTapUp(details);
}
#override
bool debugMode() => true;
#override
void render(Canvas canvas) {
super.render(canvas);
_pickerWorld.render(canvas);
}
#override
void resize(Size size) {
super.resize(size);
_pickerWorld.resize(size);
}
#override
void update(double t) {
super.update(t);
_pickerWorld.update(t);
}
}
class PickerWorld extends Box2DComponent {
final TastesVM tastes;
PickerWorld(this.tastes) : super(gravity: 0);
#override
void initializeWorld() {}
#override
void render(Canvas canvas) {
super.render(canvas);
}
Offset screenOffsetToWorldOffset(Offset position) {
return Offset(position.dx - (viewport.size.width / 2),
position.dy - (viewport.size.height / 2));
}
List<TasteVM> get checkedTastes => [
for (final component in components)
if (component is Ball && component.checked) component.model
];
void handleTap(TapUpDetails details) {
for (final component in components) {
if (component is Ball) {
final worldOffset = screenOffsetToWorldOffset(details.localPosition);
if (component.checkTapOverlap(worldOffset)) {
component.onTapUp(details);
}
}
}
}
#override
void resize(Size size) {
dimensions = Size(size.width, size.height);
viewport = box2d_viewport.Viewport(size, 1);
if (components.isEmpty) {
var tastesList = tastes.tastes;
tastesList.forEach((element) {
var ballPosOffset = Vector2(
math.Random().nextDouble() - 0.5, math.Random().nextDouble() - 0.5);
var x = ballPosOffset.x * 150;
var y = ballPosOffset.y * 150;
add(Ball(Vector2.array([x, y]), this, element));
});
}
}
}
class Ball extends BodyComponent with Tapable {
static const transitionSeconds = 0.5;
var transforming = false;
var kNormalRadius;
static const kExpandedRadius = 50.0;
var currentRadius;
var lastTapStamp = DateTime.utc(0);
final TasteVM model;
final TextConfig smallTextConfig = TextConfig(
fontSize: 12.0,
fontFamily: 'Arial',
color: Colors.white,
textAlign: TextAlign.center,
);
final TextConfig bigTextConfig = TextConfig(
fontSize: 24.0,
fontFamily: 'Arial',
color: Colors.white,
textAlign: TextAlign.center,
);
Size screenSize;
ui.Image ballImage;
bool get checked => model.checked;
Ball(
Vector2 position,
Box2DComponent box2dComponent,
this.model,
) : super(box2dComponent) {
ballImage = model.tasteimageResource;
final shape = CircleShape();
kNormalRadius = model.initialRadius;
currentRadius = (model.checked) ? kExpandedRadius : model.initialRadius;
shape.radius = currentRadius;
shape.p.x = 0.0;
// checked = model.checked;
final fixtureDef = FixtureDef();
fixtureDef.shape = shape;
fixtureDef.restitution = 0.1;
fixtureDef.density = 1;
fixtureDef.friction = 1;
fixtureDef.userData = model;
final bodyDef = BodyDef();
bodyDef.linearVelocity = Vector2(0.0, 0.0);
bodyDef.position = position;
bodyDef.type = BodyType.DYNAMIC;
bodyDef.userData = model;
body = world.createBody(bodyDef)..createFixtureFromFixtureDef(fixtureDef);
}
#override
void renderCircle(Canvas canvas, Offset center, double radius) async {
final rectFromCircle = Rect.fromCircle(center: center, radius: radius);
final ballDiameter = radius * 2;
if (ballImage == null) {
return;
}
final image = checked ? ballImage : null;
final paint = Paint()..color = const Color.fromARGB(255, 101, 101, 101);
final elapsed =
DateTime.now().difference(lastTapStamp).inMicroseconds / 1000000;
final transforming = elapsed < transitionSeconds;
if (transforming) {
_resizeBall(elapsed);
}
canvas.drawCircle(center, radius, paint);
if (image != null) {
//from: https://stackoverflow.com/questions/60468768/masking-two-images-in-flutter-using-a-custom-painter/60470034#60470034
canvas.saveLayer(rectFromCircle, Paint());
//draw the mask
canvas.drawCircle(
center,
radius,
Paint()..color = Colors.black,
);
//fit the image into the ball size
final inputSize = Size(image.width.toDouble(), image.height.toDouble());
final fittedSizes = applyBoxFit(
BoxFit.cover,
inputSize,
Size(ballDiameter, ballDiameter),
);
final sourceSize = fittedSizes.source;
final sourceRect =
Alignment.center.inscribe(sourceSize, Offset.zero & inputSize);
canvas.drawImageRect(
image,
sourceRect,
rectFromCircle,
Paint()..blendMode = BlendMode.srcIn,
);
canvas.restore();
}
final span = TextSpan(
style: TextStyle(color: Colors.white, fontSize: 10),
text: model.tasteDisplayName);
final tp = TextPainter(
text: span,
textAlign: TextAlign.center,
textDirection: TextDirection.ltr,
);
tp.layout(minWidth: ballDiameter, maxWidth: ballDiameter);
tp.paint(canvas, Offset(center.dx - radius, center.dy - (tp.height / 2)));
}
#override
void update(double t) {
final center = Vector2.copy(box.world.center);
final ball = body.position;
center.sub(ball);
var distance = center.distanceTo(ball);
body.applyForceToCenter(center..scale(1000000 / (distance)));
}
#override
ui.Rect toRect() {
var rect = Rect.fromCircle(
center: Offset(body.position.x, -body.position.y),
radius: currentRadius,
);
return rect;
}
#override
void onTapUp(TapUpDetails details) {
lastTapStamp = DateTime.now();
model.checked = !checked;
if (checked) {
currentRadius = kExpandedRadius;
} else {
currentRadius = kNormalRadius;
}
}
void _resizeBall(elapsed) {
var progress = elapsed / transitionSeconds;
final fixture = body.getFixtureList();
var sourceRadius = (checked) ? kNormalRadius : kExpandedRadius;
var targetRadius = (checked) ? kExpandedRadius : kNormalRadius;
var progressRad = ui.lerpDouble(0, math.pi / 2, progress);
var nonLinearProgress = math.sin(progressRad);
var actualRadius =
ui.lerpDouble(sourceRadius, targetRadius, nonLinearProgress);
fixture.getShape().radius = actualRadius;
}
}
This issue is a bit too wide for StackOverflow, but I'll try to answer it as well as I can.
To use Forge2D (previously box2d.dart) in Flame you have to add flame_forge2d as a dependency. From flame_forge2d you will get a Forge2DGame that you should use instead of FlameGame (and instead of the ancient BaseGame class that you are using).
After that you extend BodyComponents for each body that you want to add to your Forge2DGame.
class Ball extends BodyComponent {
final double radius;
final Vector2 _position;
Ball(this._position, {this.radius = 2});
#override
Body createBody() {
final shape = CircleShape();
shape.radius = radius;
final fixtureDef = FixtureDef(
shape,
restitution: 0.8,
density: 1.0,
friction: 0.4,
);
final bodyDef = BodyDef(
userData: this,
angularDamping: 0.8,
position: _position,
type: BodyType.dynamic,
);
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
}
In the createBody() method you have to create the Forge2D body, in this case a circle is created. If you don't want it to render the circle directly you can set renderBody = false. To render something else on top of the BodyComponent you either override the render method, or you add a normal Flame component as a child to it, for example a SpriteComponent or SpriteAnimationComponent.
To add a child, simply call add in the onLoad method (or in another fitting place):
class Ball extends BodyComponent {
...
#override
Future<void> onLoad() async {
await super.onLoad();
add(SpriteComponent(...));
}
...
}
Since you are using the Tappable mixin, you should also add the HasTappables mixin to your Forge2D game class.
You can find some examples here:
https://examples.flame-engine.org/#/flame_forge2d_Blob%20example
(press the < > in the upper right corner to get to the code).

Flutter null safety migration error Null check operator used on a null value - occured at PageTransformer ScrollMetrics object

I implemented PageView parallax effects in Flutter using github repo page-transformer .
After Null safety migration I am facing the error below.
======== Exception caught by widgets library =======================================================
The following _CastError was thrown building PageTransformer(dirty, state: _PageTransformerState#a4851):
Null check operator used on a null value
I am relatively new to Dart and Flutter, and I know very little about ScrollMetrics
Below is the code file of page_transformer.dart
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
/// A function that builds a [PageView] lazily.
typedef PageView PageViewBuilder(
BuildContext context, PageVisibilityResolver visibilityResolver);
/// A class that can be used to compute visibility information about
/// the current page.
class PageVisibilityResolver {
PageVisibilityResolver({
ScrollMetrics? metrics,
double? viewPortFraction,
}) : this._pageMetrics = metrics!, //Error here <----- When the exception was thrown, this was the stack: #0 new PageVisibilityResolver
this._viewPortFraction = viewPortFraction!;
final ScrollMetrics _pageMetrics;
final double _viewPortFraction;
PageVisibility resolvePageVisibility(int pageIndex) {
final double pagePosition = _calculatePagePosition(pageIndex);
final double visiblePageFraction =
_calculateVisiblePageFraction(pageIndex, pagePosition);
return PageVisibility(
visibleFraction: visiblePageFraction,
pagePosition: pagePosition,
);
}
double _calculateVisiblePageFraction(int index, double pagePosition) {
if (pagePosition > -1.0 && pagePosition <= 1.0) {
return 1.0 - pagePosition.abs();
}
return 0.0;
}
double _calculatePagePosition(int index) {
final double viewPortFraction = _viewPortFraction ?? 1.0;
final double pageViewWidth =
(_pageMetrics?.viewportDimension ?? 1.0) * viewPortFraction;
final double pageX = pageViewWidth * index;
final double scrollX = (_pageMetrics?.pixels ?? 0.0);
final double pagePosition = (pageX - scrollX) / pageViewWidth;
final double safePagePosition = !pagePosition.isNaN ? pagePosition : 0.0;
if (safePagePosition > 1.0) {
return 1.0;
} else if (safePagePosition < -1.0) {
return -1.0;
}
return safePagePosition;
}
}
/// A class that contains visibility information about the current page.
class PageVisibility {
PageVisibility({
required this.visibleFraction,
required this.pagePosition,
});
final double visibleFraction;
final double pagePosition;
}
class PageTransformer extends StatefulWidget {
PageTransformer({
required this.pageViewBuilder,
});
final PageViewBuilder pageViewBuilder;
#override
_PageTransformerState createState() => _PageTransformerState();
}
class _PageTransformerState extends State<PageTransformer> {
PageVisibilityResolver? _visibilityResolver;
#override
Widget build(BuildContext context) {
final pageView = widget.pageViewBuilder(
context, _visibilityResolver ?? PageVisibilityResolver());
final controller = pageView.controller;
final viewPortFraction = controller.viewportFraction;
return NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification notification) {
setState(() {
_visibilityResolver = PageVisibilityResolver(
metrics: notification.metrics,
viewPortFraction: viewPortFraction,
);
});
return false; //need a check
},
child: pageView,
);
}
}
Below is the code file of intro_page_view.dart
import 'dart:math';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:tailor_ai/screens/main/main_page.dart';
import 'package:tailor_ai/screens/product/product_page.dart';
import 'package:flutter/material.dart';
import 'package:tailor_ai/models/product.dart';
import 'page_transformer.dart';
import 'intro_page_item.dart';
class IntroPageView extends StatelessWidget {
final List<Product>? product;
final _controller = new PageController(viewportFraction: 0.85);
static const _kDuration = const Duration(milliseconds: 300);
static const _kCurve = Curves.ease;
final _kArrowColor = Colors.black.withOpacity(0.8);
IntroPageView({Key? key,this.product}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SizedBox.fromSize(
size: const Size.fromHeight(500.0),
child: PageTransformer( //<-------------- The relevant error-causing widget was: PageTransformer PageTransformer
pageViewBuilder: (context, visibilityResolver) {
return PageView.builder(
controller: _controller,
itemCount: product!.length,
itemBuilder: (context, index) {
//final item = product;
final pageVisibility =
visibilityResolver.resolvePageVisibility(index);
return InkWell(
onTap: () => Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => ProductPage(
product: product![index]
))),
child: Stack(
children: <Widget>[
IntroPageItem(product: product![index], pageVisibility: pageVisibility),
],
),
);
},
);
},
),
),
),
);
}
}
you can find the entire code files for page_transformer project in above mentioned github link which is not updated for null safety.
terminal screenshot for reference
Your valuable time in response would be much appreciated.
Here is the problem, you have this variable: final ScrollMetrics _pageMetrics; which is not nullable, on initialization, you assign it to this other variable ScrollMetrics? metrics, which is nullable. The error you get happened because metrics was null and you tried to assign it to _pageMetrics.
So why is metrics null? Well, you are supposed to pass the value of metrics on the constructor, but you didn't on this line:
final pageView = widget.pageViewBuilder(
context, _visibilityResolver ?? PageVisibilityResolver());
So the solution is to either make _pageMetrics nullable or to pass metrics to the constructor.
Pro tip: When you have a named parameter on your constructor that should always be passed (that is to say, it should never be null) you can use the required keyword:
PageVisibilityResolver({
required ScrollMetrics metrics,
required double viewPortFraction,
}) : this._pageMetrics = metrics,
this._viewPortFraction = viewPortFraction;
Of course you could also give them a default value.

How to save Mediaquery Size in a separate file in flutter?

I want to save every button and container size as mediaquery as follow in a separate dart file.
class FrameSize{
var SliderHeight = MediaQuery.of(context).size.height / 6;
var addToCartButtonHeight = MediaQuery.of(context).size.height / 14;
}
So I can call them wherever I want and change at a one place. Is that possible??
Yes it is, but you need something like a singleton. Im used to implement Get it to make this. Its simple, create an file (you can give it any name you want), Im used to call it appData, like this:
class AppData {
var isAuthenticated = false;
User user;[...] you can place whathever you want
}
A file called locator.dart (once again, name it whathever you want):
import 'package:get_it/get_it.dart';
GetIt locator = GetIt.instance;
void setupLocator() {
locator.registerLazySingleton(() => AppData());
[...] you can place oher singletons too
}
In your main.dart place this:
setupLocator();
At the begining of you main page before you call runApp()...
And now, when you want to use the data, you just do this:
class DocumentBloc extends BaseBloc (im a big fan of bloc "pattern") {
var appData = locator.get<AppData>();
//... photo = appData.user.photo;
//... locator.get<AppData>().user.photo works too...
}
This may help:
1)Create class:
import 'package:flutter/widgets.dart';
class FrameSize {
//init method is static so no object creation is required
static void init({
required context,
}) {
//instantiate variables here
_mediaQueryData = MediaQuery.of(context);
screenWidth = _mediaQueryData.size.width;
screenHeight = _mediaQueryData.size.height;
_safeAreaHorizontal =
_mediaQueryData.padding.left + _mediaQueryData.padding.right;
_safeAreaVertical =
_mediaQueryData.padding.top + _mediaQueryData.padding.bottom;
sliderHeight = screenHeight / 6;
addToCartButtonHeight = screenHeight / 14;
}
//declare variables here
static late MediaQueryData _mediaQueryData;
static late double screenWidth;
static late double screenHeight;
static late double _safeAreaHorizontal;
static late double _safeAreaVertical;
static late double sliderHeight;
static late double addToCartButtonHeight;
}
2)Instantiate it in your home screen(Do not forget).
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
FrameSize.init(context);
…
}
}
3)Finally, use it to set your widgets' dimensions.
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
MySlider(
height: FrameSize.sliderHeight,
...
),
AddToCartButton(
height: FrameSize.addToCartButtonHeight,
...
),
],
);
}
You can also refer to this article.

How can a breaking change of Flutter SDK handled in a package

https://flutter.dev/docs/release/breaking-changes/parent-data-widget-generic-type
I recently noticed this breaking change being planned for ParentDataWidget.
If the generic type of ParentDataWidget is RenderObjectWidget as should be in the current latest v1.12.13+hotfix.8 of the Stable branch, Flutter v1.13.7 and newer versions complain about it.
Is there any workaround to support both versions before and after the breaking change?
On v1.12.13+hotfix.8
class FrogSize extends ParentDataWidget<FrogJar> {
...
}
After v1.13.7
class FrogSize extends ParentDataWidget<FrogJarParentData> {
...
#override
Type get debugTypicalAncestorWidgetClass => FrogJar;
}
Try this. I used dev branch to compile the following code but hope works for your specific version too.
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'stack.dart';
class CircleContainer extends ParentDataWidget<CircleStackParentData> { //<== Changed CircleStack to CircleStackParentData
const CircleContainer({
Key key,
this.width,
this.height,
#required Widget child,
this.degree,
this.distance = 0.0,
this.rotate = 0.0,
this.align = Alignment.topLeft,
}) : assert(width == null || width > 0.0),
assert(height == null || height > 0.0),
assert(child != null),
assert(distance != null),
assert(rotate != null),
assert(align != null),
super(key: key, child: child);
final double width;
final double height;
final double distance;
final double degree;
final double rotate;
final Alignment align;
#override
void applyParentData(RenderObject renderObject) {
assert(renderObject.parentData is StackParentData);
}
#override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DoubleProperty('width', width, defaultValue: null));
properties.add(DoubleProperty('height', height, defaultValue: null));
}
static CircleContainer of(BuildContext context) {
return context.findAncestorWidgetOfExactType<CircleContainer>();
}
#override
Type get debugTypicalAncestorWidgetClass => CircleStack; //<== New added line
}
//New Added Class
class CircleStackParentData extends ParentData {
double width;
double height;
double distance;
double degree;
double rotate;
Alignment align;
}

Flutter Gallery demo: Cards to change card's height

I am trying to modify the cards_demo.dart found in Flutter examples. My purpose is that instead of having the built-in two cards' height be fixed as:
static final double height=300.0 (or some mandatory and fixed number), I want to have different height for the two cards.
So I modified the TravelDestination class to include a property height:
class TravelDestination {
const TravelDestination({ this.assetName, this.title, this.description, this.height });
final String assetName;
final String title;
final List<String> description;
final double height;
bool get isValid => assetName != null && title != null && description?.length == 3;
}
Then, in class TravelDestinationItem build function:
class TravelDestinationItem extends StatelessWidget {
TravelDestinationItem({ Key key, #required this.destination }) : super(key: key) {
assert(destination != null && destination.isValid);
}
static final double height = 512.0;
final TravelDestination destination;
#override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
final TextStyle titleStyle = theme.textTheme.headline.copyWith(color: Colors.white);
final TextStyle descriptionStyle = theme.textTheme.subhead;
return new Container(
padding: const EdgeInsets.all(8.0),
height: destination.height,
//height: height,
child: new Card(
child: new Column(... ...
I assigned different height property to the two cards but the result is not working: they are still the same height as specified by static final double height.
If I comment out static final double height line, the compiler will remind me: No static getter 'height' declared...
I am very much confused on this behavior.
Can anyone help?
Since you're using items of varying height, you should remove this line from the call to the ListView constructor:
itemExtent: TravelDestinationItem.height,
Also, you'll need to hot-restart the app (hot-reloading won't update the destinations list with the new data, since it's a global variable).