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;
}
Related
Based on this repo: https://gist.github.com/bluemix/4c932e8c4a1cd6f497a4353d9e536f57
Class:
class AnimatedCount extends ImplicitlyAnimatedWidget {
const AnimatedCount({
Key? key,
required this.count,
Duration duration = const Duration(milliseconds: 600),
Curve curve = Curves.fastOutSlowIn,
}) : super(duration: duration, curve: curve, key: key);
final num count;
#override
ImplicitlyAnimatedWidgetState<ImplicitlyAnimatedWidget> createState() {
return _AnimatedCountState();
}
}
class _AnimatedCountState extends AnimatedWidgetBaseState<AnimatedCount> {
IntTween _intCount = IntTween(begin: 0, end: 1);
Tween<double> _doubleCount = Tween<double>();
#override
Widget build(BuildContext context) {
return widget.count is int
? Text(_intCount.evaluate(animation).toString())
: Text(_doubleCount.evaluate(animation).toStringAsFixed(2));
}
#override
void forEachTween(TweenVisitor<dynamic> visitor) {
if (widget.count is int) {
_intCount = visitor(
_intCount,
widget.count,
(dynamic value) => IntTween(begin: value),
) as IntTween;
} else {
_doubleCount = visitor(
_doubleCount,
widget.count,
(dynamic value) => Tween<double>(begin: value),
) as Tween<double>;
}
}
}
I'm trying to pass data:
AnimatedCount(count: getKcal()), //as Text Widget
getKcal function is easy (as for testing purposes):
double getKcal() {
return 10;
}
However I receive an exception here (on the Class AnimatedCount):
_doubleCount = visitor(
Exception has occurred.
_CastError (type 'Null' is not a subtype of type 'double' in type cast)
No problems when I use integers. The Class does not accept doubles and I'm not finding why...
I have a similar widget for CupertinoPicker, but it was made by hand. I need to make the middle element of the list be selected by default, not the initial one as it is now. I've tried different options but haven't been able to do it yet. Please tell me how to implement this, I will be grateful for the help?
widget
class WheelSpeedPicker extends StatefulWidget {
final Function(dynamic) onValueChanged;
final int? startPosition;
final double itemSize;
final double? listHeight;
final int currentPosition;
final double? listWidth;
final FixedExtentScrollController? controller;
const WheelSpeedPicker({
Key? key,
required this.onValueChanged,
required this.currentPosition,
required this.itemSize,
this.startPosition,
this.listHeight,
this.controller,
this.listWidth,
}) : super(key: key);
#override
_WheelTimePickerState createState() {
return _WheelTimePickerState();
}
}
class _WheelTimePickerState extends State<WheelSpeedPicker> {
FixedExtentScrollController? fixedExtentScrollController;
int? currentPosition;
#override
void initState() {
super.initState();
currentPosition = widget.currentPosition;
fixedExtentScrollController = widget.controller ??
FixedExtentScrollController(initialItem: currentPosition ?? 0);
}
void _listener(int position) {
setState(() {
currentPosition = position;
});
widget.onValueChanged(currentPosition);
}
#override
void initState() {
super.initState();
// swap this
// currentPosition = widget.currentPosition;
// with this
currentPosition = (time.length / 2).floor();
fixedExtentScrollController = widget.controller ??
FixedExtentScrollController(initialItem: currentPosition ?? 0);
}
Other than that, I think your widget would be more useful if you added the items list to the widget parameters:
class WheelSpeedPicker extends StatefulWidget {
final Function(dynamic) onValueChanged;
final List<String> items;
final int? startPosition;
final double itemSize;
final double? listHeight;
final int currentPosition;
final double? listWidth;
final FixedExtentScrollController? controller;
const WheelSpeedPicker({
Key? key,
required this.items,
required this.onValueChanged,
required this.currentPosition,
required this.itemSize,
this.startPosition,
this.listHeight,
this.controller,
this.listWidth,
}) : super(key: key);
And then you'd have a more coherent set of parameters, because you already have a startPosition in the interface.
#override
void initState() {
super.initState();
currentPosition = widget.startPosition;
fixedExtentScrollController = widget.controller ??
FixedExtentScrollController(initialItem: startPosition ?? 0);
}
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.
I want to rotate an image based on the heading/direction of my device, This class produces the MarkerIcon widget
import 'package:location/location.dart';
class MarkerOption {
final MarkerIcon? defaultMarker;
final MarkerIcon? advancedPickerMarker;
MarkerOption({
this.defaultMarker,
this.advancedPickerMarker,
});
MarkerOption copyWith({
MarkerIcon? defaultMarker,
MarkerIcon? advancedPickerMarker,
}) {
return MarkerOption(
defaultMarker: defaultMarker ?? this.defaultMarker,
advancedPickerMarker:
advancedPickerMarker ?? this.advancedPickerMarker);
}
}
class MarkerIcon extends StatelessWidget {
final Icon? icon;
final AssetImage? image;
MarkerIcon({
this.icon,
this.image,
Key? key,
}) : assert(icon != null || image != null),
super(key: key);
#override
Widget build(BuildContext context) {
Widget? child = SizedBox.shrink();
if (icon != null) {
child = icon;
} else if (image != null)
child = Image(
image: image!,
);
return child!;
}
}
I am using the Location Plugin that allows you to add the rotation attributes to the widget like this
MarkerIcon( rotation: currentLocation.heading )This way the image will point to which ever direction my device is pointing.But I don't know how to use it in this class. How can I add these attributes to the class such that I can call it like this?
markerIcon: MarkerIcon(
image: AssetImage(
"images/images/arrow.png",
rotation: position.heading,
),
)
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).