-
-
Notifications
You must be signed in to change notification settings - Fork 180
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
state.value = newIndex is not causing a re-build of the widget even though newIndex is different #434
Comments
Can u please check on this? This looks like a critical issue in useState... |
I'm in holiday. But try: useEffect(() {
Future(() => onTabChanged(activeIndex.value));
}, [activeIndex.value]);
I'm pretty sure you have an exception somewhere in your code because of this useEffect. It's violating some widget rules, which would lead to Flutter not updating the UI. |
Can someone pls tell me which widget rules is violated? This is the full code I pasted which can be run in any project. |
I hardcoded missing data on your example because it's not reproducible as it is. When I make it run and press on the tab buttons this error appears:
So, when PracticeDetailScreen is still building there is another child widget triggering a rebuild in PracticeDetailScreen again. This code "just works" meaning that I'm not 100% sure if these hooks are meant to be used like that, sorry I come from react and I din't find any documentation about this but the reasoning is this:
and the code is this: import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class PracticeDetailScreen extends HookConsumerWidget {
// I don't know what `YogaPracticeId` is so I leave it hardcoded.
final int practiceId;
// I put a default value, with 0 you trigger a empty response.
const PracticeDetailScreen({super.key, this.practiceId = 1});
@override
Widget build(BuildContext context, WidgetRef ref) {
// The data it's not important in this case so we can leave it hardcoded
final practiceData = practiceId != 0 ? null : 'some data';
final activeIndex = useState<int>(0);
if (practiceData == null) {
return const Scaffold(
body: Center(
child: Text('Practice not found'),
),
);
}
// Removed unncessesary dependencies
return Scaffold(
body: SafeArea(
child: Column(
children: [
TabBarRow(activeIndex: activeIndex),
activeIndex.value == 0
? const IntroTabView()
: const MeditateTabView()
],
),
),
);
}
}
class TabBarRow extends StatelessWidget {
// Here we receive the state `activeIndex` from the parent that is of type ValueNotifier<int>
final ValueNotifier<int> activeIndex;
const TabBarRow({super.key, required this.activeIndex});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 20),
height: 50,
padding: const EdgeInsets.symmetric(horizontal: 5),
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
side: const BorderSide(width: 1, color: Color(0xFFE8EAEB)),
borderRadius: BorderRadius.circular(6),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: TabBarButton(
onPressed: () {
activeIndex.value = 0;
},
isActive: activeIndex.value == 0,
title: 'Intro',
),
),
Expanded(
child: TabBarButton(
onPressed: () {
activeIndex.value = 1;
},
isActive: activeIndex.value == 1,
title: 'Meditate'),
),
],
),
);
}
}
class TabBarButton extends StatelessWidget {
const TabBarButton(
{super.key,
required this.onPressed,
required this.isActive,
this.title = 'Intro'});
final bool isActive;
final VoidCallback onPressed;
final String title;
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
onPressed();
},
child: Container(
height: 40,
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
decoration: ShapeDecoration(
color: isActive ? const Color(0xFF1FA1AA) : null,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
title,
style: TextStyle(
color: isActive ? Colors.white : Colors.grey,
fontSize: 14,
fontFamily: 'Open Sans',
fontWeight: FontWeight.w600,
height: 0,
),
),
],
),
),
);
}
}
class IntroTabView extends HookConsumerWidget {
const IntroTabView({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Container(
height: 100,
width: 50,
color: Colors.blue,
child: const Text('intro'),
);
}
}
class MeditateTabView extends HookConsumerWidget {
const MeditateTabView({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return const Text('meditate');
}
}
I would investigate a bit more if I were you, maybe some experienced flutter dev can help, but in the meantime it works. |
Describe the bug
Calling activeIndex.value = newIndex is not causing a re-build of the widget.
To Reproduce
Full Reproducible code :
Package versions :
hooks_riverpod: ^2.5.1
flutter_hooks: ^0.20.5
riverpod_annotation: ^2.3.5
Expected behavior
Clicking on the "Intro" button or "Meditate" button should show the corresponding IntroTabView and MeditateTabView .
The text was updated successfully, but these errors were encountered: