-
Notifications
You must be signed in to change notification settings - Fork 55
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
Watch doesn't trigger rebuild on signal if used in a nested Builder #363
Comments
This should be equivalent! Not sure why one is working and the other is not. Do you have a complete example to test with? |
It's quite strange that when i made a simple counter app example, it worked flawlessly. but for if you can read it out idk if i'm doing anything wrong in this app here but that's the page i'm working on atm i redacted many irrelevant widgets for you to read easier final selectedPostSignal = Ref.scoped(
(_) => signal<SocialPost?>(null, debugLabel: 'selectedPostSignal'),
autoDispose: false,
);
final selectedProfileSignal = Ref.scopedFamily(
(_, int customerId) => futureSignal<SocialProfile>(
() async {
return Supabase.instance.client
.from('social_profiles')
.select('*, posts:social_posts(*)')
.eq('customer_id', customerId)
.single()
);
},
debugLabel: 'selectedProfileSignal',
),
autoDispose: false,
);
class SocialProfileScreen extends StatefulWidget {
final int? customerId;
const SocialProfileScreen({super.key, this.customerId});
@override
State<SocialProfileScreen> createState() => _SocialProfileScreenState();
}
class _SocialProfileScreenState extends State<SocialProfileScreen>
with SignalsMixin {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'Social management',
style: context.textTheme.displaySmall
?.copyWith(fontWeight: FontWeight.w600),
),
const Gap(16),
Expanded(
child: Row(
children: [
Watch.builder(
builder: (context) {
final profileSignal =
selectedProfileSignal.of(context, widget.customerId);
final selectedPost = selectedPostSignal.of(context).value;
return profileSignal.value.map(
loading: () => CenterLoading(
indicatorColor: context.colorScheme.primary,
),
error: (error) => Center(child: Text('Error: $error')),
data: (profile) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SocialTabs(),
ReorderableGridView.builder(
itemBuilder: (context, index) {
final post = posts.value[index];
return Stack(
key: ValueKey(index),
children: [
PostCard(
post: post,
isPinned: post.isPinned,
onTap: (post) => selectedPostSignal
.of(context)
.value = post,
),
if (selectedPost?.id == post.id)
Text(post.postDateFormatted),
],
);
},
),
],
),
);
},
),
const Gap(16),
Watch.builder(
builder: (context) {
final selectedPost = selectedPostSignal.of(context);
if (selectedPost.value == null) return const SizedBox();
return const Expanded(child: CommentsView());
},
),
],
),
),
],
);
}
} so if we zoom in on the ReordableGridView that's the working example where i have ReorderableGridView.builder(
itemBuilder: (context, index) {
final post = posts.value[index];
return Stack(
key: ValueKey(index),
children: [
PostCard(
post: post,
isPinned: post.isPinned,
onTap: (post) => selectedPostSignal
.of(context)
.value = post,
),
if (selectedPost?.id == post.id)
Text(post.postDateFormatted),
],
);
},
), but if I changed it to be it never get updated from the |
That is because .of(context) is inside a builder context for the list view and not a Watch.builder. The signal would not be updated because it is only reading the value and not bound to that context. You could try .watch(context) |
You are right! it's because of the listview context not the Watch.builder, however it's not about the .of(context) i removed it in the example is got the weird behaviour
here's the counter app example import 'package:flutter/material.dart';
import 'package:signals_flutter/signals_flutter.dart';
final selectedCounter = signal<int?>(null, debugLabel: 'selectedCounter');
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
_counter++;
if (_counter == 3) {
selectedCounter.value = 3;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Watch.builder(
debugLabel: 'counter widget',
builder: (context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
// Comment these two lines to watch the strange behavior
if (selectedCounter.watch(context) == _counter)
const Text('Without Builder: EQUAL'),
Builder(builder: (_) {
if (selectedCounter.watch(context) == _counter) {
return const Text('With Builder: EQUAL');
} else {
return const Text('With Builder: NOT EQUAL');
}
})
],
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
} |
there's another weird behaviour i have found when using signals in such way
so i have declared my signals as usual
and in my widget down below i'm using
if i did this it would never work and never rebuild if somewhere else i changed selectedPostSignal
i have to assign it to a variable to work
and volla!, magically it works.
please validate this with me rody, if that's a malpractice of me.
The text was updated successfully, but these errors were encountered: