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

fix: 3417 The tagline blinks in the app language, then goes back to English #3426

Merged
merged 6 commits into from
Dec 12, 2022

Conversation

omkarChend1kar
Copy link
Contributor

What

Fixes bug(s)

@omkarChend1kar omkarChend1kar requested a review from a team as a code owner December 9, 2022 16:23
@omkarChend1kar omkarChend1kar changed the title fix: #3417 The tagline blinks in the app language, then goes back to English fix: 3417 The tagline blinks in the app language, then goes back to English Dec 9, 2022
if (data.connectionState != ConnectionState.done ||
data.data == null ||
!data.hasData) {
if (data.data == null || !data.hasData) {
Copy link
Contributor

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.

Copy link
Contributor Author

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.

@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.

Copy link
Contributor

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 a Text instead (a stateLESSwidget)?

Copy link
Contributor Author

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.
Waiting state

Copy link
Contributor

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?

Copy link
Contributor Author

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?

@monsieurtanuki Yeah, This is what ideally should be done.

Copy link
Contributor

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.

Copy link
Contributor Author

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.

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 by waiting.

Copy link
Contributor

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".

Copy link
Contributor Author

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.

Copy link
Contributor

@monsieurtanuki monsieurtanuki left a 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)

Comment on lines 298 to 299
///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.
Copy link
Contributor

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) {
Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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

Copy link
Contributor

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 with context.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 the Future in a variable, like we do in main.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,
...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@codecov-commenter
Copy link

codecov-commenter commented Dec 9, 2022

Codecov Report

Merging #3426 (f3342a5) into develop (69f902e) will decrease coverage by 0.00%.
The diff coverage is 0.00%.

@@             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     
Impacted Files Coverage Δ
...mooth_app/lib/widgets/smooth_product_carousel.dart 2.45% <0.00%> (-0.07%) ⬇️

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

Copy link
Contributor

@monsieurtanuki monsieurtanuki left a 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!

@monsieurtanuki monsieurtanuki merged commit 1301896 into openfoodfacts:develop Dec 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

The tagline blinks in the app language, then goes back to English
4 participants