The value of local variable isn't used - flutter

I am new to flutter and I was following a tutorial when this error popped up. The ancestorRenderObjectOfType has deprecated and been replaced by findAncestorRenderObjectOfType so dart is throwing me errors.
What the tutor did in his video of old dart:
static T of<T extends BlocBase>(BuildContext context) {
final type = _typeOf<BlocProvider<T>>();
BlocProvider<T> provider = context
.context.ancestorRenderObjectOfType(type);
return provider.bloc;
}
static Type _typeOf() => T;
}
What I did in my code
static T of<T extends BlocBase>(BuildContext context) {
final type = _typeOf<BlocProvider<T>>();
BlocProvider<T> provider = context
.findAncestorRenderObjectOfType();
return provider.bloc;
}
static Type _typeOf<T>() => T;
}
If I put
BlocProvider<T> provider = context
.findAncestorRenderObjectOfType(type);
I get an error saying
Too many positional arguements. 0 expected, but 1 found.
The ENTIRE code
abstract class BlocBase {
void dispose();
}
//Genric Bloc provider
class BlocProvider<T extends BlocBase> extends StatefulWidget {
BlocProvider({
Key key,
#required this.child,
#required this.bloc,
}) : super(key: key);
final T bloc;
final Widget child;
#override
_BlocProviderState<T> createState() => _BlocProviderState<T>();
static T of<T extends BlocBase>(BuildContext context) {
final type = _typeOf<BlocProvider<T>>();
BlocProvider<T> provider = context
.findAncestorRenderObjectOfType(); //context.ancestorRenderObjectOfType(type); GOTCHA
return provider.bloc;
}
static Type _typeOf<T>() => T;
}
class _BlocProviderState<T> extends State<BlocProvider<BlocBase>> {
#override
void dispose() {
widget.bloc.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return widget.child;
}
}
Thank you!

findAncestorRenderObjectOfType doesnt take the type as argument instead it is a generic method where you can provide the type while calling the method. So your code will be as below:
static T of<T>(BuildContext context) {
BlocProvider<T> provider = context
.findAncestorRenderObjectOfType<BlocProvider<T>>();
return provider.bloc;
}

Related

Generic Widget for listening Streams in Flutter

I would like to create a StatefulWidget which I'll use all over the app for listening streams of different types. Since I try to keep all the widgets Stateless I wanted to extract this functionality.
I've created this:
class StreamListener<T> extends StatefulWidget {
const StreamListener({
Key? key,
required this.stream,
required this.onNewData,
required this.child,
}) : super(key: key);
final Stream<T?> stream;
final void Function(T data) onNewData;
final Widget child;
#override
State<StreamListener> createState() => _StreamListenerState<T>();
}
class _StreamListenerState<T> extends State<StreamListener> {
late StreamSubscription<T?> streamSubscription;
#override
void initState() {
streamSubscription = (widget.stream as Stream<T?>).listen(
(T? data) {
if (data != null) {
widget.onNewData(data);
}
},
);
super.initState();
}
#override
Widget build(BuildContext context) {
return widget.child;
}
#override
void dispose() {
streamSubscription.cancel();
super.dispose();
}
}
Then somewhere in the Widgets tree I use:
return StreamListener<int>(
stream: context.read<MyCubit>().toastStream,
onNewData: (int data) {
print("Received: $data");
},
child: SomeStatelessWidget(),
}
Stream logic is added to the Cubit like that:
mixin ToastStreamForCubit<T> {
final StreamController<T> _toastStreamController = StreamController<T>();
get toastStream => _toastStreamController.stream;
void emitToastEvent(T event) {
_toastStreamController.add(event);
}
}
And when I call let's say emitToastEvent(1).
I receive type '(int) => void' is not a subtype of type '(dynamic) => void'.
on line widget.onNewData(data);.
I'm not sure what is going on. I thought I've mapped all the functions and classes to a particular generic type (T), but it still says something about dynamic.
You are missing T while extending State<StreamListener>. It should be
class _StreamListenerState<T> extends State<StreamListener<T>>

Flutter assign generic Future<T> from function callback

Why does not generic Future<T> assign work? i Get this error: A value of type 'Future<T>?' can't be assigned to a variable of type 'Future<T>?'.
typedef SimpleFutureFunction<T> = Widget Function(void Function(Future<T>? newFuture) onFuture);
class SimpleFuture<T> extends StatefulWidget {
const SimpleFuture({Key? key, required this.simple, this.future}) : super(key: key);
final SimpleFutureFunction simple;
final Future<T>? future;
#override
State<SimpleFuture> createState() => _SimpleFutureState();
}
class _SimpleFutureState<T> extends State<SimpleFuture<T>> {
Future<T>? _future;
#override
void initState() {
super.initState();
_future = widget.future;
}
#override
Widget build(BuildContext context) {
return widget.simple.call(<T>(Future<T>? newFuture) {
setState(() {
_future = newFuture;
});
});
}
}

I want to change the height of the appbar using the value of json style file

I have a app_bar_base.dart file where i have an AppBar.
class AppBarBase extends StatelessWidget implements PreferredSizeWidget {
late double appBarHeight = LoadAppStyle().loadAppStyle();
AppBarBase({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return AppBar();
}
#override
Size get preferredSize => Size.fromHeight(appBarHeight);
}
I am calling the method LoadAppStyle().loadAppStyle() from the file load_app_style:
class LoadAppStyle {
loadAppStyle() async {
String jsonData =
await rootBundle.loadString('assets/config/app_style.json');
Map<String, dynamic> data = jsonDecode(jsonData);
var getHeight = double.parse(data["app_bar"]["app_bar_height"]);
return getHeight;
}
}
In the load_app_style.dart file i grab the value of app_bar_heigt from the app_style.json
in app_style.json i have key app_bar_height where i want to change the value manually to change the height of the App bar
{
"app_bar":
{
"app_bar_height": 78
},
}
But for some reason i get the error : type 'Future<dynamic>' is not a subtype of type 'double'
You can add the type to your loadAppStyle method. Since your method is async it returns a Future.
Future<double> loadAppStyle() async {
...
return getHeight;
}
Now your error should be
type 'Future<double>' is not a subtype of type 'double'
Since your method returns a Future you have to use await to get the value.
loadAppStyle() // Future<double>
await loadAppStyle() // double
If you want to use a value of a Future inside a Widget, have a look at FutureBuilder.
For your case you could e.g. use the FutureBuilder to retrieve the height and then pass it to AppBarBase
FutureBuilder<double>(
future: loadAppStyle(),
builder: (context, snapshot) {
if(snapshot.hasData) {
return AppBarBase(height: snapshot.data);
} else {
return const Center(child: CirclularProgressIndicator));
}
}
)
And change your AppBarBase to the following.
class AppBarBase extends StatelessWidget implements PreferredSizeWidget {
AppBarBase({
Key? key,
required this.height,
}) : super(key: key);
final double height;
#override
Widget build(BuildContext context) {
return AppBar();
}
#override
Size get preferredSize => Size.fromHeight(height);
}
In your example, loadAppStyle() has no defined return type (dynamic) and it is marked as async (Future), hence the return type of this function is Future<dynamic>. Size.fromHeight function requires the double value, hence you get this error - the expected type is double, but Future<dynamic> was found here.
To resolve the type differences, you should set the return type of a function:
class LoadAppStyle {
Future<double> loadAppStyle() async {
String jsonData =
await rootBundle.loadString('assets/config/app_style.json');
Map<String, dynamic> data = jsonDecode(jsonData);
var getHeight = double.parse(data["app_bar"]["app_bar_height"]);
return getHeight;
}
}
Now, since your function is async, you must wait for your Future to finish and only then you could retrieve the double value. It would look something like this:
class AppBarBase extends StatelessWidget implements PreferredSizeWidget {
late double appBarHeight = await LoadAppStyle().loadAppStyle(); // Throws error
AppBarBase({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return AppBar();
}
#override
Size get preferredSize => Size.fromHeight(appBarHeight);
}
However, this throws an error since you cannot use the asynchronous code when initialising a value this way. What could be a better way to do this is to wait for this value somewhere outside of your widget and pass the result via the constructor:
class AppBarBase extends StatelessWidget implements PreferredSizeWidget {
final double appBarHeight;
AppBarBase({
required this.appBarHeight,
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return AppBar();
}
#override
Size get preferredSize => Size.fromHeight(appBarHeight);
}
This way, you separate your UI code from the widget. Anyway, the way of keeping this UI-specific configuration inside the JSON file sounds way overengineered - consider just passing this value via constructor directly, like: AppBarBase(appBarHeight: 78).

Error: Type argument 'T' doesn't conform to the bound 'Object' of the type variable 'T' on 'GetIt.call'. After migrating to Null Safety

I'm in the process of migrating over a large project to null safety and I'm coming across a strange error I'm not entirely sure how to fix.
"Error: Type argument 'T' doesn't conform to the bound 'Object' of the type variable 'T' on 'GetIt.call'."
class BaseView<T extends BaseProvider?> extends StatefulWidget {
final Widget Function(BuildContext context, T value, Widget? child)? builder;
final Function(T)? onModelReady;
BaseView({this.builder, this.onModelReady});
#override
_BaseViewState<T> createState() => _BaseViewState<T>();
}
class _BaseViewState<T extends BaseProvider?> extends State<BaseView<T?>> {
T model = locator<T>(); <---- This is throwing it
#override
void initState() {
if (widget.onModelReady != null) {
widget.onModelReady!(model);
}
super.initState();
}
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<T?>(
create: (context) => model,
child: Consumer<T>(builder: widget.builder!),
);
}
}
I can't find much info on this error and so far any method I've tried hasn't worked out. Can anyone be of assistance?
I'm using Provider for state management and BaseView is what wraps all my other views during build; e.g.:
class EquipmentMainView extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BaseView<EquipmentProvider>(
onModelReady: (model) async {
model.getAllFunctions();
},..
Posting here for anyone else that might eventually run across this in the future, just changed the nullability of BaseProvider suggested by jamesdlin by changing
class BaseView<T extends BaseProvider?>
to
class BaseView<T extends BaseProvider>
I had similar issues when I upgraded to flutter 2.0 I made the generic methods in Generic class to explicitly extends Base class Object i.e
from:
import 'package:get_it/get_it.dart';
class PoultryBabaRegistry<T> {
static GetIt _getIt = GetIt.instance;
static void register<T>(T model) {
_getIt.registerSingleton<T extends Object >(model, signalsReady: true);
}
static void remove<T>(T model) {
_getIt.unregister<T extends Object>(instance:model);
}
static T getIt<T>() {
return _getIt.get<T>();
}
}
to:
class PoultryBabaRegistry<T extends Object> {
static GetIt _getIt = GetIt.instance;
static void register<T extends Object>(T model) {
_getIt.registerSingleton<T >(model, signalsReady: true);
}
static void remove<T extends Object>(T model) {
_getIt.unregister<T>(instance:model);
}
static T getIt<T extends Object>() {
return _getIt.get<T>();
}
}

Flutter: Are RenderObjects recreated during lifetime of a StatefulWidget?

I have a setup where a StatefulWidget creates a custom (leaf) RenderBox. Is there are possibility that the RenderBox is disposed and recreated during the lifetime of the State?
Background. I want to keep certain results of heavy calculations/animation controllers in the RenderObject, that's why it is important for me that the RenderObject lives as long as the State of the enclosing widget.
import 'package:flutter/widgets.dart';
class SquaredCircle extends StatefulWidget {
const SquaredCircle({Key key, this.radius}) : super(key: key);
final double radius;
#override
State<StatefulWidget> createState() => _SquaredCircleState();
}
class _SquaredCircleState extends State<SquaredCircle> {
int _squareness = 42;
#override
Widget build(BuildContext context) {
final foo = widget.radius * _squareness;
return _SquaredCircleRenderObjectWidget(foo);
}
}
class _SquaredCircleRenderObjectWidget extends LeafRenderObjectWidget {
_SquaredCircleRenderObjectWidget(this.foo);
final double foo;
#override
_RenderSquaredCircle createRenderObject(BuildContext context) {
return _RenderSquaredCircle(foo: foo);
}
#override
void updateRenderObject(BuildContext context, _RenderSquaredCircle renderObject) {
renderObject..foo = foo;
}
}
class _RenderSquaredCircle extends RenderBox {
_RenderSquaredCircle({double foo})
: assert(foo != null),
_foo = foo;
double get foo => _foo;
double _foo;
set foo(double value) {
assert(value != null);
if (_foo == value) return;
_foo = value;
markNeedsPaint();
}
// ...
}
A RenderObject will not be disposed until you replace it by another RenderObject of a different type, or remove the associated widget from the widget tree.