Skip to content

Commit

Permalink
Update: translate doc french (#3194)
Browse files Browse the repository at this point in the history
Hi @rrousselGit,

* This PR enhances the French documentation by incorporating additional
translations from the latest English documentation.
* The focus is on the 'cookbook' section.
  • Loading branch information
GitGud31 authored Dec 12, 2023
1 parent bad3209 commit cce8768
Show file tree
Hide file tree
Showing 10 changed files with 518 additions and 3 deletions.
6 changes: 3 additions & 3 deletions website/i18n/fr/docusaurus-plugin-content-docs/current.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@
"message": "Application Todo avec fonction de sauvegarde et de restauration",
"description": "The label for link Todo App with Backup and Restore feature in sidebar Sidebar, linking to https://github.com/TheAlphaApp/flutter_riverpod_todo_app"
},
"sidebar.Sidebar.link.Integrating Hive database with Riverpod (simple example)": {
"message": "Integration d'une base de donnée Hive avec Riverpod (exemple simple)",
"description": "The label for link Integrating Hive database with Riverpod (simple example) in sidebar Sidebar, linking to https://github.com/GitGud31/theme_riverpod_hive"
"sidebar.Sidebar.link.Integrating Hive database with Riverpod": {
"message": "Integration d'une base de donnée Hive avec Riverpod",
"description": "The label for link Integrating Hive database with Riverpod in sidebar Sidebar, linking to https://github.com/GitGud31/flutter_theme_riverpod_hive"
},
"sidebar.Sidebar.link.Browser App with Riverpod": {
"message": "Application navigateur avec Riverpod",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
title: Pull-to-refresh / Retry-on-error
---

import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";

Dans ce guide, nous verrons comment les Providers peuvent être utilisés pour mettre
en œuvre facilement une fonction de "pull-to-refresh" ou de "retry-on-error"
(tentative de réessai en cas d'erreur).
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
---
titre : Recherche au fur et à mesure de la frappe
---
Un exemple concret pourrait être l'utilisation de `FutureProvider` pour implémenter une barre de recherche.

## Exemple d'utilisation : Barre de recherche "Search as we type" (recherche au fur et à mesure de la frappe)

La mise en œuvre d'une "recherche au fil de la frappe" peut sembler décourageante au début si l'on utilise des moyens conventionnels.
Il y a beaucoup d'éléments en jeu, tels que

- le traitement des erreurs.
- le débouclage (debouncing) de l'entrée de l'utilisateur afin d'éviter de faire des demandes de réseau à chaque frappe.
- l'annulation des demandes de réseau en attente lorsque le champ de recherche change.

Mais la combinaison de `FutureProvider` et de la puissance de [ref.watch] peut considérablement simplifier cette tâche.

Un schéma courant pour effectuer des requêtes asynchrones est de la diviser en plusieurs providers :

- un [StateNotifierProvider] ou `StateProvider` pour les paramètres de votre requête (ou alternativement utiliser [family])
- un `FutureProvider`, qui effectuera la requête en lisant les paramètres des autres providers/[family].

La première étape consiste à stocker l'entrée de l'utilisateur quelque part. Pour cet exemple, nous utiliserons `StateProvider` (puisque l'état de la recherche n'est qu'une simple `chaîne`) :

```dart
final searchInputProvider = StateProvider<String>((ref) => '');
```

Nous pouvons ensuite connecter ce provider à un [TextField] en faisant :

```dart
Consumer(
builder: (context, ref, child) {
return TextField(
onChanged: (value) => ref.read(searchInputProvider.notifier).state = value,
);
},
)
```

Ensuite, nous pouvons créer notre `FutureProvider` qui s'occupera de la requête :

```dart
final searchProvider = FutureProvider<
<!--
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
final searchFieldProvider = StateProvider<String>((ref) => '');
final questionsProvider = FutureProvider<List>((ref) async {
final client = http.Client();
ref.onDispose(client.close);
final search = ref.watch(searchFieldProvider);
Uri uri;
if (search.isEmpty) {
uri = Uri.parse(
'https://api.stackexchange.com/2.3/questions?order=desc&sort=activity&site=stackoverflow',
);
} else {
final encodedQuery = Uri.encodeComponent(search);
uri = Uri.parse(
'https://api.stackexchange.com/2.3/search?order=desc&sort=activity&intitle=$encodedQuery&site=stackoverflow',
);
}
final response = await client.get(uri);
final questions = jsonDecode(response.body);
return questions['items'].map((question) => question['title']).toList();
});
void main() => runApp(const ProviderScope(child: MyApp()));
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(home: MyHomePage());
}
}
class MyHomePage extends ConsumerWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final questions = ref.watch(questionsProvider);
return Scaffold(
appBar: AppBar(title: const Text('Questions')),
body: Column(
children: [
TextField(
onChanged: (value) =>
ref.read(searchFieldProvider.notifier).state = value,
),
Expanded(
child: questions.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, stack) => Center(child: Text('Error $error')),
data: (questions) {
return ListView.builder(
itemCount: questions.length,
itemBuilder: (context, index) {
final question = questions[index];
return ListTile(
title: Text(
question.toString(),
),
);
},
);
},
),
),
],
),
);
}
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';

class FakeRepository {}

final repositoryProvider = Provider((ref) => FakeRepository());

abstract class Todo {
String get id;
String get label;
bool get completed;
}

final todoListProvider = FutureProvider<List<Todo>>((ref) => []);

void main() {
/* SNIPPET START */

test('override repositoryProvider', () async {
final container = ProviderContainer(
overrides: [
// Surcharge le comportement de repositoryProvider pour qu'il renvoie
// FakeRepository au lieu de Repository.
/* highlight-start */
repositoryProvider.overrideWithValue(FakeRepository())
/* highlight-end */
// Il n'est pas nécessaire de surcharger `todoListProvider`,
// il utilisera automatiquement le repositoryProvider surchargé.
],
);

// La première lecture si l'état est en cours de chargement
expect(
container.read(todoListProvider),
const AsyncValue<List<Todo>>.loading(),
);

/// Attendre la fin de la demande (la requete)
await container.read(todoListProvider.future);

// Expose les données recherchées
expect(container.read(todoListProvider).value, [
isA<Todo>()
.having((s) => s.id, 'id', '42')
.having((s) => s.label, 'label', 'Hello world')
.having((s) => s.completed, 'completed', false),
]);
});

/* SNIPPET END */
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return Container();
}
}

final repositoryProvider = Provider((ref) => FakeRepository());

class FakeRepository {}

void main() {
/* SNIPPET START */

testWidgets('override repositoryProvider', (tester) async {
await tester.pumpWidget(
ProviderScope(
overrides: [
// Surcharge le comportement de repositoryProvider pour qu'il renvoie
// FakeRepository au lieu de Repository.
/* highlight-start */
repositoryProvider.overrideWithValue(FakeRepository())
/* highlight-end */
// Il n'est pas nécessaire de surcharger `todoListProvider`,
// il utilisera automatiquement le repositoryProvider surchargé.
],
child: const MyApp(),
),
);
});

/* SNIPPET END */
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';

class Repository {
Future<List<Todo>> fetchTodos() async => [];
}

class Todo {
Todo({
required this.id,
required this.label,
required this.completed,
});

final String id;
final String label;
final bool completed;
}

// Nous exposons notre instance de référentiel dans un provider
final repositoryProvider = Provider((ref) => Repository());

/// La liste des todos. Ici, nous les récupérons simplement sur
/// le serveur en utilisant [Repository] et ne faisons rien d'autre.
final todoListProvider = FutureProvider((ref) async {
// Obtention de l'instance Repository
final repository = ref.read(repositoryProvider);

// Récupérer les todos et les exposer à l'interface utilisateur.
return repository.fetchTodos();
});

/// Une implémentation simulée de Repository qui renvoie une liste prédéfinie de todos.
class FakeRepository implements Repository {
@override
Future<List<Todo>> fetchTodos() async {
return [
Todo(id: '42', label: 'Hello world', completed: false),
];
}
}

class TodoItem extends StatelessWidget {
const TodoItem({super.key, required this.todo});
final Todo todo;
@override
Widget build(BuildContext context) {
return Text(todo.label);
}
}

void main() {
testWidgets('override repositoryProvider', (tester) async {
await tester.pumpWidget(
ProviderScope(
overrides: [
repositoryProvider.overrideWithValue(FakeRepository()),
],
// Notre application, qui lira le todoListProvider pour afficher la liste des todos à faire.
// Vous pouvez l'extraire dans un widget MyApp
child: MaterialApp(
home: Scaffold(
body: Consumer(builder: (context, ref, _) {
final todos = ref.watch(todoListProvider);
// La liste des todos est en cours de chargement ou en erreur
if (todos.asData == null) {
return const CircularProgressIndicator();
}
return ListView(
children: [
for (final todo in todos.asData!.value) TodoItem(todo: todo)
],
);
}),
),
),
),
);

// Le premier frame est un état de chargement.
expect(find.byType(CircularProgressIndicator), findsOneWidget);

// Re-render. TodoListProvider devrait avoir fini de récupérer les todos maintenant
await tester.pump();

// Plus de chargement
expect(find.byType(CircularProgressIndicator), findsNothing);

// Rendu d'un TodoItem avec les données renvoyées par FakeRepository
expect(tester.widgetList(find.byType(TodoItem)), [
isA<TodoItem>()
.having((s) => s.todo.id, 'todo.id', '42')
.having((s) => s.todo.label, 'todo.label', 'Hello world')
.having((s) => s.todo.completed, 'todo.completed', false),
]);
});
}
Loading

0 comments on commit cce8768

Please sign in to comment.