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

If AsyncLoading state is set it doesn't change later #3330

Closed
Maryusz opened this issue Feb 10, 2024 · 4 comments
Closed

If AsyncLoading state is set it doesn't change later #3330

Maryusz opened this issue Feb 10, 2024 · 4 comments
Assignees
Labels
bug Something isn't working question Further information is requested

Comments

@Maryusz
Copy link

Maryusz commented Feb 10, 2024

I have issues to switch from AsyncLoading to AsyncData or AsyncError, once the state is set to AsyncLoading it remains in it. If I remove the state = const AsyncLoading(); the code works as expected.

import 'dart:math';

import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'test_provider.g.dart';

@riverpod
class Test extends _$Test {
  @override
  FutureOr<List<int>> build() {
    return [];
  }

  Future<void> addRandomNumber() async {
    state = const AsyncLoading();
    try {
      await Future.delayed(const Duration(seconds: 2));

      final random = Random();
      final number = random.nextInt(100);
      state = AsyncData([...await future, number]);
    } catch (error, stack) {
      state = AsyncError(error, stack);
    }
  }
}

Even using the alternative values:

import 'dart:math';

import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'test_provider.g.dart';

@riverpod
class Test extends _$Test {
  @override
  FutureOr<List<int>> build() {
    return [];
  }

  Future<void> addRandomNumber() async {
    state = const AsyncValue.loading();
    try {
      // await Future.delayed(const Duration(seconds: 2));

      final random = Random();
      final number = random.nextInt(100);
      state = AsyncValue.data([...await future, number]);
    } catch (error, stack) {
      state = AsyncValue.error(error, stack);
    }
  }
}

Or the AsyncValue.guard:

import 'dart:math';

import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'test_provider.g.dart';

@riverpod
class Test extends _$Test {
  @override
  FutureOr<List<int>> build() {
    return [];
  }

  Future<void> addRandomNumber() async {
    state = const AsyncValue.loading();
    state = await AsyncValue.guard(() async {
      await Future.delayed(const Duration(seconds: 2));

      return [...await future, Random().nextInt(100)];
    });
  }
}

The provider should be in the Loading state then, after executing the logic, switch to AsyncData or AsyncError, is this intended or I'm doing something wrong?

@Maryusz Maryusz added bug Something isn't working needs triage labels Feb 10, 2024
@rrousselGit
Copy link
Owner

You'll have to be more specific. You should share the code that's consuming your provider too.
As it is, I cannot know what the issue is.

@rrousselGit rrousselGit added question Further information is requested and removed needs triage labels Feb 10, 2024
@Maryusz
Copy link
Author

Maryusz commented Feb 10, 2024

Sure,

main.dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:new_test_app/test_page.dart';

void main() {
  runApp(
    const ProviderScope(
      child: MainApp(),
    ),
  );
}

class MainApp extends ConsumerStatefulWidget {
  const MainApp({super.key});

  @override
  ConsumerState<MainApp> createState() => _MainAppState();
}

class _MainAppState extends ConsumerState<MainApp> {
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: TestPage());
  }
}

test_page.dart which is a simple widget I'm using to test the behaviour:

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter/material.dart';
import 'package:new_test_app/test_provider.dart';

class TestPage extends ConsumerWidget {
  const TestPage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final test = ref.watch(testProvider);

    ref.listen(testProvider, (prev, next) {
      next.when(data: (data) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('Data: $data'),
          ),
        );
      }, error: (error, stack) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('Error: $error'),
          ),
        );
      }, loading: () {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(
            content: Text('Loading...'),
          ),
        );
      });
    });

    return Scaffold(
      appBar: AppBar(
        title: const Text('Test Page'),
      ),
      body: Center(
        child: Column(
          children: [
            const Text('Test Page'),
            Text('Test Provider: $test'),
            Text('Test Provider: ${ref.watch(testProvider).value}'),
            Text('Test Provider: ${ref.watch(testProvider).requireValue}'),
            test.when(
              data: (data) => Text('Data: $data'),
              error: (error, stack) => Text('Error: $error'),
              loading: () => const CircularProgressIndicator(),
            ),
            ElevatedButton(
              onPressed: () {
                ref.read(testProvider.notifier).addRandomNumber();
              },
              child: const Text('Add Random Number'),
            ),
          ],
        ),
      ),
    );
  }
}

And the yaml file:

name: new_test_app
description: A new Flutter project.
publish_to: 'none'
version: 0.1.0

environment:
  sdk: '>=3.1.3 <4.0.0'

dependencies:
  flutter:
    sdk: flutter
  flutter_riverpod: ^2.4.6
  freezed: ^2.4.5
  freezed_annotation: ^2.4.1
  riverpod_annotation: ^2.3.1

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^2.0.0
  riverpod_generator: ^2.3.6
  build_runner: ^2.4.6
  custom_lint: ^0.5.6
  riverpod_lint: ^2.3.4

flutter:
  uses-material-design: true

@rrousselGit
Copy link
Owner

Oh, right, that's expected.

await future waits until you're out of a loading state to get you the latest data. But you entered "loading" right before calling it.

By doing:

state = AsyncLoading();
await future;

You're effectively forever waiting for a state update, because nothing else in your code would set state to a non-loading state.

You could obtain the previous state before entering "loading":

final previous = await future;
state = AsyncLoading();

Or not use future:

final previous = state.requireValue;
state = AsyncLoading();

@Patrick386
Copy link

Patrick386 commented Apr 28, 2024

This issue needs to be reopened. Even though the 'state = AsyncValue.data(...)' status has been updated, it continues to remain in the loading state. The loading state should automatically stop once the data loading is complete, but it doesn't seem to be happening.

The loading state works fine the first time when the provider is initialized. However, after that, it needs to be manually triggered.

The position of 'state = const AsyncValue.loading();' doesn't matter.

My case:
flutter_riverpod: ^2.5.1
riverpod_generator: ^2.4.0

@riverpod
class ItemPagingController extends _$ItemPagingController {
  @override
  Future<DataState<ItemData>> build() async {
  ...
  hitsSearcher.responses.listen(_fetchDataUpdate);
  return state.value ?? DataState();
}

Future<void> _fetchDataUpdate(SearchResponse response) async {
    try {
        // data loading: on
       state = AsyncValue.data(await update((s) =>
          s.copyWith(
            items: [...s.items, ...items],    
          )));
       // data loading: off   
          
      // The position of 'state = const AsyncValue.loading();' doesn't matter.    
      // state = const AsyncValue.loading();
      
    } catch (error, stack) {
      logger.info('>>> fetch error:${error.toString()}');
      state = AsyncValue.error(error, stack);
    }
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants