I'm learning proper error handling, I feel THIS is the best possible method of handling errors I've found. Problem is that the project is old as hell and not updated for null safety.
Project works well with old version. Tell me how I can get over this compile time error:
The argument type 'Either<Failure, dynamic>' can't be assigned to the parameter type 'Either<Failure, Post>'
This occurs due to labeling _post in PostChangeNotifier class as "late"...
import 'dart:convert';
import 'dart:io';
import 'package:dartz/dartz.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ProviderScope(
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.orange,
),
home: const HomePage(),
),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blueGrey,
body: Flex(
direction: Axis.horizontal,
children: [Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Consumer(
builder: (ctx, ref, child) {
if (ref.read(notifier).state == NotifierState.initial) {
return const Text('Press the button');
} else if (ref.read(notifier).state == NotifierState.loading) {
return const CircularProgressIndicator();
} else {
return ref.read(notifier).post.fold(
(failure) => Text(failure.toString()),
(post) => Text(post.toString()),
);
}
},
),
Consumer(
builder: (ctx, ref,child) {
return ElevatedButton(
onPressed: () {
ref.read(notifier).getOnePost();
},
child: const Text('Get Post'));
},
),
]),
),]
),
);
}
}
class PostService {
final httpClient = FakeHttpClient();
Future<Post?> getOnePost() async {
try {
final responseBody = await httpClient.getResponseBody();
return Post.fromJson(responseBody);
} on SocketException {
throw Failure('No Internet connection ');
} on HttpException {
throw Failure("Couldn't find the post ");
} on FormatException {
throw Failure("Bad response format ");
}
}
}
class FakeHttpClient {
Future<String> getResponseBody() async {
await Future.delayed(const Duration(milliseconds: 500));
//! No Internet Connection
// throw SocketException('No Internet');
//! 404
// throw HttpException('404');
//! Invalid JSON (throws FormatException)
// return 'abcd';
return '{"userId":1,"id":1,"title":"nice title","body":"cool body"}';
}
}
enum NotifierState { initial, loading, loaded }
final notifier = ChangeNotifierProvider((ref) => PostChangeNotifier());
class PostChangeNotifier extends ChangeNotifier {
final _postService = PostService();
NotifierState _state = NotifierState.initial;
NotifierState get state => _state;
void _setState(NotifierState state) {
_state = state;
notifyListeners();
}
late Either<Failure, Post> _post;
Either<Failure, Post> get post => _post;
void _setPost(Either<Failure, Post> post) {
_post = post;
notifyListeners();
}
void getOnePost() async {
_setState(NotifierState.loading);
await Task(() => _postService.getOnePost())
.attempt()
.mapLeftToFailure()
.run()
.then((value) => _setPost(value)); /// error occurs here
_setState(NotifierState.loaded);
}
}
extension TaskX<T extends Either<Object, U>, U> on Task<T> {
Task<Either<Failure, U>> mapLeftToFailure() {
return map(
(either) => either.leftMap((obj) {
try {
return obj as Failure;
} catch (e) {
throw obj;
}
}),
);
}
}
class Post {
final int id;
final int userId;
final String title;
final String body;
Post({
required this.id,
required this.userId,
required this.title,
required this.body,
});
static Post? fromMap(Map<String, dynamic> map) {
return Post(
id: map['id'],
userId: map['userId'],
title: map['title'],
body: map['body'],
);
}
static Post? fromJson(String source) => fromMap(json.decode(source));
@override
String toString() {
return 'Post id: $id, userId: $userId, title: $title, body: $body';
}
}
class Failure {
// Use something like "int code;" if you want to translate error messages
final String message;
Failure(this.message);
@override
String toString() => message;
}