-
-
Notifications
You must be signed in to change notification settings - Fork 313
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
fix: 3417 The tagline blinks in the app language, then goes back to English #3426
fix: 3417 The tagline blinks in the app language, then goes back to English #3426
Conversation
if (data.connectionState != ConnectionState.done || | ||
data.data == null || | ||
!data.hasData) { | ||
if (data.data == null || !data.hasData) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You probably didn't write the initial code but that is redundant as:
bool get hasData => data != null;
In addition to that, the data.connectionState != ConnectionState.done
(or ==
, whatever) must be dealt with explicitly, as this is the only thing that really matters in AsyncSnapshot
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You probably didn't write the initial code but that is redundant as:
bool get hasData => data != null;In addition to that, the
data.connectionState != ConnectionState.done
(or==
, whatever) must be dealt with explicitly, as this is the only thing that really matters inAsyncSnapshot
.
@monsieurtanuki Then we will have to handle ConnectionState.waiting
in some other way .let say some sort of loading indicator or shimmer effect, But that too would be just replacing default tagline text with some other thing, Which will cause blinking issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then we will have to handle ConnectionState.waiting in some other way.
@omkarChend1kar I don't understand everything but if you have to, you have to.
It's not just a matter of fixing a bug, it's a matter of making code maintainable. And the first thing I would say if I had to read the code is: "where is done
dealt with?".
In addition to that, your specific fix doesn't make any sense in a first approach, as dealing with AsyncSnapshot
in the standard lazy way would consider that data.connectionState != ConnectionState.done
, data.data == null
and !data.hasData
are the same thing (which is correct unless the data returned is actually null
).
Besides, when it's done
it's done
, it doesn't look like it can go back to waiting
later.
/// Connected to a terminated asynchronous computation.
done,
My questions:
- are you able to reproduce the bug?
- does your fix actually fixes the bug?
- would you consider replacing your fix with a more reasonable
data.connectionState != ConnectionState.done
test? - I have a hunch that the problem comes from the
AutoSizeText
(a stateFULwidget that gets refreshed) - could you try with aText
instead (a stateLESSwidget)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@monsieurtanuki I am not sure why it's emitting connectionState.waiting
during lifecycle changes, With data being not null whereas documentation suggests otherwise https://api.flutter.dev/flutter/widgets/ConnectionState.html and this is essentially what causing the blinking issue, That's why only thing, I could think was to remove the flag.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then you're not lucky:
waiting, indicating that the asynchronous operation has begun, typically with the data being null.
Again, I would suggest not to rely on data.hasData
(especially as it's unexpectedly populated while still waiting
) but only on data.connectionState != ConnectionState.done
.
You want to display data when it's complete, don't you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then you're not lucky:
waiting, indicating that the asynchronous operation has begun, typically with the data being null.
Again, I would suggest not to rely on
data.hasData
(especially as it's unexpectedly populated while stillwaiting
) but only ondata.connectionState != ConnectionState.done
. You want to display data when it's complete, don't you?
@monsieurtanuki Yeah, This is what ideally should be done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@omkarChend1kar Part of the problem is that you're displaying that within a Consumer
that gets refreshed.
Could you add a print
statement at the beginning of method _fetchData
?
I suspect this method gets called several times, and that would cause the blinking.
If it's not that, you need to investigate "in the literature" in which conditions done
can be followed by waiting
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@omkarChend1kar Part of the problem is that you're displaying that within a
Consumer
that gets refreshed. Could you add a_fetchData
? I suspect this method gets called several times, and that would cause the blinking.
Yeah, It's quite possible, Will check, Thank You @monsieurtanuki!!
If it's not that, you need to investigate "in the literature" in which conditions
done
can be followed bywaiting
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If so, a solution would be to wait until done
, to store the data in a private field and to display it.
If refreshed, we should still display the private field until the next done
where the private field value would be replaced.
That said, I also suggest that you use a different language (if you don't already), as part of the PR is about "then goes back to English".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@monsieurtanuki Yes indeed ,You are correct, Consumer is rebuilding entire FutureBuilder
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @omkarChend1kar!
Please have a look at my comments; from what I've understood:
- we don't need the
Consumer
at all, and getting rid of it would simply fix the bug (just do that and you're done) - we should not call again and again
fetchData
, or just once or from time to time and store the result for the current locale (I think you should create a low priority issue for that)
///This is to avoid blinking issue caused, Due to [ConnectionState.done] followed by [ConnectionState.waiting] | ||
///When [Consumer] refreshes the [FutureBuilder] causing [_fetchData()] to be called again. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I've understood things, this description is a bit misleading: done
is never followed by waiting
inside the same AsyncSnapshot
. The Consumer
does call again _fetchData
, and that is what should be said first.
if (data.data![TAG_LINE_KEY] != null) { | ||
///This is to avoid blinking issue caused, Due to [ConnectionState.done] followed by [ConnectionState.waiting] | ||
///When [Consumer] refreshes the [FutureBuilder] causing [_fetchData()] to be called again. | ||
if (tagline != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should not store a TagLineItem
, but a Map<String, dynamic>
instead, because you're currently missing the _SearchCardTagLineDeprecatedAppText
case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just had a deeper look at fetchData
: you should not call it again if the locale
did not change (which is very likely). it makes absolutely no sense to call the server again and again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have good news: you probably don't need the Consumer
at all, as the only added value (?) it provides is setting a default value in one specific case. And the default value in the other cases is also the same value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have good news: you probably don't need the
Consumer
at all, as the only added value (?) it provides is setting a default value in one specific case. And the default value in the other cases is also the same value.
@monsieurtanuki I wasn't able to see the tagline untill, I did my first scan.I guess we will need UserPreferences
instance to check the same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@omkarChend1kar Actually you're right (I won't discuss here the functional added value of waiting for the first scan).
That said, we may agree that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops...
@omkarChend1kar Looking again at the code, this is what I would suggest:
- replace the
Consumer<UserPreferences>
syntax withcontext.watch<UserPreferences>()
- not because it's smarter but because it is consistent with the rest of the app, for maintenance purposes - run the
fetchData
only once by storing theFuture
in a variable, like we do inmain.dart
// We store the argument of FutureBuilder to avoid re-initialization on
// subsequent builds. This enables hot reloading. See
// https://github.com/openfoodfacts/smooth-app/issues/473
late Future<void> _initFuture;
@override
void initState() {
super.initState();
_initFuture = _init2();
}
Future<bool> _init2() async {
//...
}
@override
Widget build(BuildContext context) {
return FutureBuilder<void>(
future: _initFuture,
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@monsieurtanuki Done!
Codecov Report
@@ Coverage Diff @@
## develop #3426 +/- ##
===========================================
- Coverage 11.30% 11.29% -0.01%
===========================================
Files 260 260
Lines 12557 12561 +4
===========================================
Hits 1419 1419
- Misses 11138 11142 +4
📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @omkarChend1kar for your rewriting, that looks much easier to read now!
What
snapshot.connectionState == ConnectionState.done
, During lifecycle changes when it's in stateConnectionState.waiting
it displays default tagline text, Even though snapshot has data.Fixes bug(s)