Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to Watch Optional Property #8

Closed
cliftonlabrum opened this issue Aug 22, 2023 · 5 comments
Closed

How to Watch Optional Property #8

cliftonlabrum opened this issue Aug 22, 2023 · 5 comments

Comments

@cliftonlabrum
Copy link

I have an optional selectedFlight property in a class like this:

class ModelFlight with ChangeNotifier {
  //+++
  static ModelFlight get to => di<ModelFlight>();
  //+++

  Flight? _selectedFlight;
  Flight? get selectedFlight => _selectedFlight;
  set selectedFlight(Flight? value) {
    _selectedFlight = value;

    notifyListeners();
  }
}

...but it crashes when I set up the property value watcher in a widget:

Widget build(BuildContext context) {
  watchPropertyValue((ModelFlight model) => model.selectedFlight); //<-- !! Exception !!
}

_TypeError (Null check operator used on a null value)

How can I watch a nullable property that won't be set until later?

@escamoteur
Copy link
Owner

can you paste the full stacktrace of when this happens? Might be a bug in watch_it

@cliftonlabrum
Copy link
Author

Here is the stack trace:

════════ Exception caught by widgets library ═══════════════════════════════════
The following _TypeError was thrown building MyApp(dirty):
Null check operator used on a null value

The relevant error-causing widget was
MyApp
main.dart:6
When the exception was thrown, this was the stack
#0      watchPropertyValue
watch_it.dart:156
#1      MyApp.build
main.dart:15
#2      StatelessElement.build
framework.dart:5367
#3      _GetItElement.build
elements.dart:20
#4      ComponentElement.performRebuild
framework.dart:5297
#5      Element.rebuild
framework.dart:5016
#6      ComponentElement._firstBuild
framework.dart:5279
#7      ComponentElement.mount
framework.dart:5273
#8      _GetItElement.mount
elements.dart:10
...     Normal element mounting (27 frames)
#35     Element.inflateWidget
framework.dart:4182
#36     Element.updateChild
framework.dart:3707
#37     RenderObjectToWidgetElement._rebuild
binding.dart:1253
#38     RenderObjectToWidgetElement.mount
binding.dart:1222
#39     RenderObjectToWidgetAdapter.attachToRenderTree.<anonymous closure>
binding.dart:1169
#40     BuildOwner.buildScope
framework.dart:2719
#41     RenderObjectToWidgetAdapter.attachToRenderTree
binding.dart:1168
#42     WidgetsBinding.attachRootWidget
binding.dart:1001
#43     WidgetsBinding.scheduleAttachRootWidget.<anonymous closure>
binding.dart:981
#47     _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:189:12)
(elided 3 frames from class _Timer and dart:async-patch)
════════════════════════════════════════════════════════════════════════════════

It looks like the crash is on line 156 of watch_it.dart where you are force-unwrapping observedProperty (which, in my case, is null at the moment).

assert(observedProperty! is! Listenable, 'selectProperty returns a Listenable. Use watchIt instead');

Here is a simple project reproducing it:

import 'package:flutter/material.dart';
import 'package:watch_it/watch_it.dart';

void main() {
  di.registerSingleton<Test>(Test());
  runApp(const MyApp());
}

class MyApp extends StatelessWidget with WatchItMixin {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    //~~~
    watchPropertyValue((Test x) => x.selectedThing);
    //~~~
    return MaterialApp(
      title: 'Watch It Test',
      home: Column(
        children: [
          Text('Testing: ${Test.to.selectedThing}'),
          TextButton(
            onPressed: () {
              final thing = Thing(name: 'hi');
              Test.to.selectedThing = thing;
            },
            child: const Text('Set'),
          ),
        ],
      ),
    );
  }
}

class Test with ChangeNotifier {
  static Test get to => di<Test>();

  Thing? _selectedThing;
  Thing? get selectedThing => _selectedThing;
  set selectedThing(Thing? value) {
    _selectedThing = value;
    notifyListeners();
  }
}

class Thing {
  String name;

  Thing({
    required this.name,
  });
}

(As a side note, I use Test.to.selectedThing in my widget instead of final thing = watchPropertyValue((Test x) => x.selectedThing); because it makes reactive variables easier to find in my widget code. 😊)

@escamoteur
Copy link
Owner

Fixed in V1.0.0

thomas-watchenterprise pushed a commit that referenced this issue Aug 24, 2023
@cliftonlabrum
Copy link
Author

The fix works great! Thank you! 😄

@escamoteur
Copy link
Owner

escamoteur commented Aug 24, 2023 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants