import 'notifier_gaps.dart'; import 'provider_setup.dart'; /// Matched only through typed variable patterns. An object-pattern-only matcher /// reads `PatternA()` but not `PatternA value`, so it would wrongly call both /// variants unrendered - this function is the regression guard for that. /// /// The wildcard is needed because PatternState is a plain abstract union, not a /// sealed one: the compiler cannot know it is closed. That mirrors the real /// nuwayze case this rule was fixed against. Object? patternView(PatternState state) => switch (state) { PatternA value => value, PatternB value => value, _ => null, }; /// A provider declaration, so the collector can see which types are Listenable. final liveProvider = ChangeNotifierProvider( create: (_) => LiveNotifier(), ); /// A provider for an immutable type, created once outside any build. Together /// with [PassesConfigView] below this is the negative control for /// `provider-created-in-build`: a `Provider` exists, but both uses go /// through `.value`, so neither may be reported. final appConfigProvider = Provider.value(value: const AppConfig()); /// BAD: reads a ChangeNotifier without subscribing inside build, so this widget /// will not rebuild when the notifier changes. class ReadInBuildView { const ReadInBuildView(); Object build(BuildContext context) { final live = context.read(); return live.value; } } /// BAD: the same mistake through Provider.of(..., listen: false). class ProviderOfView { const ProviderOfView(); Object build(BuildContext context) { return Provider.of(context, listen: false).value; } } /// GOOD: subscribes, so it rebuilds. class WatchInBuildView { const WatchInBuildView(); Object build(BuildContext context) { return context.watch(); } } /// GOOD: an immutable service is not a Listenable, so not subscribing is /// correct and a rule that flagged every context.read would be wrong here. class ServiceInBuildView { const ServiceInBuildView(); Object build(BuildContext context) { return context.read(); } } /// GOOD: read in a callback, where not rebuilding is exactly what you want. class CallbackReadView { const CallbackReadView(); Object build(BuildContext context) => const Object(); void onTap(BuildContext context) { context.read().setValue(1); } } class Button { const Button({required this.onPressed}); final void Function() onPressed; } /// GOOD: the read is inside a closure passed to a widget, so it runs later, not /// while this build method runs. class ClosureReadView { const ClosureReadView(); Object build(BuildContext context) { return Button( onPressed: () => context.read().setValue(1), ); } } /// Consumed by the UI, but only `value` is displayed: `unusedFlag` is dead state. class PartiallyReadView { const PartiallyReadView(); Object build(BuildContext context) { return context.watch().value; } } /// BAD: a provider constructed inside build without `.value`, so a new notifier /// is created on every rebuild. class CreatesProviderView { const CreatesProviderView(); Object build(BuildContext context) { return ChangeNotifierProvider(create: (_) => LiveNotifier()); } } /// GOOD: `.value` passes an instance created once. class PassesValueView { const PassesValueView(this.notifier); final LiveNotifier notifier; Object build(BuildContext context) { return ChangeNotifierProvider.value(value: notifier); } } /// GOOD: an immutable value passed with `.value` inside build. /// /// This and [appConfigProvider] are the negative controls for /// `provider-created-in-build`: the type `AppConfig` is provided, but only via /// `.value`, which passes an instance created once. class PassesConfigView { const PassesConfigView(this.config); final AppConfig config; Object build(BuildContext context) { return Provider.value(value: config); } }