Subscribe our newsletter
Search
Close this search box.
Latest posts
Get notified of the Latest Sport News Update from Our Blog
Share

In Flutter, as in Dart, a Future represents a value or an error that will be available at some point in the future. It is a core part of Dart’s asynchronous programming model and is used extensively for tasks that take time to complete, such as fetching data from the internet, reading files, or interacting with a database.

Key Characteristics of Futures

  1. Asynchronous Operation: A Future is used to handle operations that are asynchronous, meaning they might take some time to complete and the result isn’t available immediately.
  2. Single Completion: A Future completes with either a value (successful result) or an error (failure). Once a future is completed, it cannot change its state.
  3. Await and Async: The await keyword is used to pause the execution of an async function until the future completes. This allows writing asynchronous code in a more synchronous, readable manner.

Creating and Using Futures

Creating a Future

There are several ways to create a Future in Dart:

1. Using Future.value and Future.error:

Future.value creates a Future that completes with the provided value. This is useful when you want to simulate an asynchronous operation that successfully returns a result.

Example:

Suppose you have a function that fetches user data. For testing purposes, you can mock this function to return a Future that immediately completes with a mock user data.

import 'dart:async';

void main() {
  // Simulate fetching user data
  Future<Map<String, String>> fetchUserData() {
    // Mock user data
    Map<String, String> userData = {'name': 'John Doe', 'email': 'john.doe@example.com'};
    return Future.value(userData);
  }

  // Usage example
  fetchUserData().then((data) {
    print('User Name: ${data['name']}');
    print('User Email: ${data['email']}');
  });
}

Output:
User Name: John Doe
User Email: john.doe@example.com

2. Using Future.error

Future.error creates a Future that completes with an error. This is useful for simulating error conditions in asynchronous operations.

Example:

Continuing with the user data fetching example, you can mock the function to return a Future that immediately completes with an error.

import 'dart:async';

void main() {
  // Simulate fetching user data with an error
  Future<Map<String, String>> fetchUserDataWithError() {
    return Future.error('Failed to fetch user data');
  }

  // Usage example
  fetchUserDataWithError().catchError((error) {
    print('Error: $error');
  });
}

Output:
Error: Failed to fetch user data

Combining Future.value and Future.error in Tests

In a real-world application, you often need to test both success and failure scenarios. Here’s how you can use Future.value and Future.error to mock different outcomes.

Example:

Consider a repository class with a method to fetch user data. In tests, you can mock this method to return either a successful result or an error.

class UserRepository {
  Future<Map<String, String>> fetchUserData() {
    // This would normally make a network call
    return Future.value({'name': 'John Doe', 'email': 'john.doe@example.com'});
  }

  Future<Map<String, String>> fetchUserDataWithError() {
    // Simulate a network error
    return Future.error('Network error occurred');
  }
}

void main() {
  final repository = UserRepository();

  // Test successful data fetch
  repository.fetchUserData().then((data) {
    print('Success: ${data['name']}');
  }).catchError((error) {
    print('Unexpected Error: $error');
  });

  // Test data fetch with error
  repository.fetchUserDataWithError().then((data) {
    print('Unexpected Success: ${data['name']}');
  }).catchError((error) {
    print('Expected Error: $error');
  });
}

Output:
Success: John Doe
Expected Error: Network error occurred

2. Using Future.delayed:

Future.delayed is a Dart utility that allows you to create a Future that completes after a specified duration. This can be useful for simulating delays in asynchronous operations, such as network requests.

Useful in testing scenarios where you need to simulate network latency or other delays.

Example
Simulating a Network Request with Delay
import 'dart:async';

void main() {
  // Function to simulate fetching user data with a delay
  Future<Map<String, String>> fetchUserData() {
    return Future.delayed(Duration(seconds: 2), () {
      return {'name': 'John Doe', 'email': 'john.doe@example.com'};
    });
  }

  // Usage example
  print('Fetching user data...');
  fetchUserData().then((data) {
    print('User Name: ${data['name']}');
    print('User Email: ${data['email']}');
  }).catchError((error) {
    print('Error: $error');
  });
}

.here, fetchUserData is defined to return a Future that completes after a 2-second delay with a mock user data map. Future.delayed Creates a Future that completes after the specified duration (Duration(seconds: 2)).

Then and CatchError:
  • .then((data) { ... }): Executes when the Future completes successfully, printing the user data.
  • .catchError((error) { ... }): Handles any errors that occur (though in this example, no errors are thrown).
Output
Fetching user data...
(after 2-second delay)
User Name: John Doe
User Email: john.doe@example.com

3. Using the async and await keywords:

The async keyword is used to mark a function as asynchronous. When you define a function with async, it returns a Future and allows you to use await within it.

The await keyword is used to pause the execution of an async function until the awaited Future completes. This means the function will wait for the result of the Future before proceeding to the next line of code.

Example: Fetching a Value Asynchronously
import 'dart:async';

// Function to simulate fetching a value with a delay
Future<int> fetchValue() async {
  await Future.delayed(Duration(seconds: 2));  // Simulate a delay of 2 seconds
  return 42;  // Return a value after the delay
}

void main() async {
  print('Fetching value...');
  try {
    int value = await fetchValue();  // Await the completion of fetchValue
    print('Fetched value: $value');
  } catch (e) {
    print('Error: $e');
  }
}

  1. async Keyword: Marks a function as asynchronous, allowing the use of await inside it.
  2. await Keyword: Pauses the function execution until the awaited Future completes.
  3. Future.delayed: Simulates a delay of 2 seconds.
  4. fetchValue Function: Asynchronously waits for 2 seconds and then returns 42.
  5. main Function:
    • Prints “Fetching value…”
    • Waits for fetchValue to complete and prints the fetched value.
Output
Fetching value...
(after a 2-second delay)
Fetched value: 42

Handling Future Results

There are two primary ways to handle the results of a Future: using then and catchError or using async and await.

1. Handling Future Results: Using then and catchError:
import 'dart:async';

// Function to simulate fetching user data
Future<Map<String, String>> fetchUserData() {
  return Future.delayed(Duration(seconds: 2), () {
    // Simulated user data
    return {'name': 'John Doe', 'email': 'john.doe@example.com'};
  });
}

void main() {
  print('Fetching user data...');
  
  // Using then to handle successful result
  fetchUserData().then((data) {
    print('User Name: ${data['name']}');
    print('User Email: ${data['email']}');
  }).catchError((error) {
    print('Error: $error');
  });
}

Output
Fetching user data...
User Name: John Doe
User Email: john.doe@example.com

fetchUserData Function: Returns a Future that completes after a 2-second delay with simulated user data.

Main Function:

  • Prints “Fetching user data…” to indicate the start of the asynchronous operation.
  • Uses then to handle a successful result from fetchUserData. The provided callback prints the user’s name and email.
  • Uses catchError to handle any errors that occur during the asynchronous operation. The error message is printed in this case.
2. Handling Future Results: Using async and await
import 'dart:async';

// Function to simulate fetching user data
Future<Map<String, String>> fetchUserData() {
  return Future.delayed(Duration(seconds: 2), () {
    // Simulated user data
    return {'name': 'John Doe', 'email': 'john.doe@example.com'};
  });
}

void main() async {
  print('Fetching user data...');

  try {
    // Await the result of fetchUserData
    Map<String, String> data = await fetchUserData();
    print('User Name: ${data['name']}');
    print('User Email: ${data['email']}');
  } catch (error) {
    print('Error: $error');
  }
}

Output
Fetching user data...
User Name: John Doe
User Email: john.doe@example.com
  1. fetchUserData Function: Same as in the previous example, returns a Future with simulated user data after a 2-second delay.
  2. Main Function:
    • Prints “Fetching user data…” to indicate the start of the asynchronous operation.
    • Uses try-catch to handle both successful results and errors.
    • await fetchUserData() waits for fetchUserData to complete and returns the result.
    • If an error occurs during the asynchronous operation, it is caught and printed in the catch block.

Leave a Reply

Your email address will not be published. Required fields are marked *

Related blogs

Nullam quis risus eget urna mollis ornare vel eu leo. Aenean lacinia bibendum nulla sed 

Subscribe for coding tips and tutorials!