Flutter高级BLoC和服务操作

在这篇文章中,我将解释Bloc和Vexana库的使用,它是一个状态管理解决方案,处于高级水平。我在这个项目中也使用了Provider。我将在下一篇文章中对此进行解释。

Vexana是一个由 Veli Bacık 基于Dio库之上编写的。它对服务操作非常有用。
与其他库不同。

我们创建模型类的方法如下:

import 'package:vexana/vexana.dart';

class QuoteModel extends INetworkModel<QuoteModel> {
  String? id;
  String? author;
  String? content;
  List<String>? tags;
  String? authorSlug;
  int? length;
  String? dateAdded;
  String? dateModified;

  QuoteModel({
    this.id,
    this.author,
    this.content,
    this.tags,
    this.authorSlug,
    this.length,
    this.dateAdded,
    this.dateModified,
  });

  
  QuoteModel fromJson(Map<String, dynamic> json) => QuoteModel.fromJson(json);

  factory QuoteModel.fromJson(Map<String, dynamic> json) {
    return QuoteModel(
      id: json['_id'] as String?,
      author: json['author'] as String?,
      content: json['content'] as String?,
      tags: (json['tags'] as List<dynamic>?)?.map((e) => e as String).toList(),
      authorSlug: json['authorSlug'] as String?,
      length: json['length'] as int?,
      dateAdded: json['dateAdded'] as String?,
      dateModified: json['dateModified'] as String?,
    );
  }

  
  Map<String, dynamic>? toJson() => _toJson();

  Map<String, dynamic> _toJson() {
    return {
      'id': id,
      'author': author,
      'content': content,
      'tags': tags,
      'authorSlug': authorSlug,
      'length': length,
      'dateAdded': dateAdded,
      'dateModified': dateModified,
    };
  }
}

这里唯一的区别是一个从INetworkModel延伸出来的模型类。

你可以使用这个链接,它也支持Vexana的Json到Dart操作:https://dartj.web.app/#/

现在我们可以开始编写服务操作了。

class VexanaManager extends NetworkManager {
  VexanaManager()
      : super(
            options: BaseOptions(
              baseUrl: AppConstants.baseUrl,
              followRedirects: true,
            ),
            isEnableLogger: true,
            isEnableTest: true);
}

就像在Dio库中一样,我们在这里定义NetworkManager类。它给了我们一些选择。我们以后将使用这个VexanaManager类。这是一个重要的定义。

让我们继续!

abstract class IQuoteService {
  final INetworkManager networkManager;

  IQuoteService(this.networkManager);

  Future<QuoteModel> getAllQuotes();
}

class QuoteService extends IQuoteService {
  QuoteService(INetworkManager networkManager) : super(networkManager);

  
  Future<QuoteModel> getAllQuotes() async {
    var response = await networkManager.send(
      AppConstants.baseUrl,
      parseModel: QuoteModel(),
      method: RequestType.GET,
    );
    return response.data;
  }
}

现在让我们仔细研究一下。
我们有一个名为IQuoteService的抽象类。
在命名时,抽象类通常以I开头,在这里我们将定义我们的方法。我们可以不创建这个抽象类而直接在一个具体的类上定义它,但这不是最佳做法。

无论如何,首先我们从INetworkManager中创建一个对象(就像从Dio库中创建一个对象)。INetworkManager类是Vexana库带给我们的一个类。

接下来,定义唯一的方法,这个应用程序的唯一服务是要从API中获取数据。

现在我们创建一个具体的类,在这个类中我们将定义这个方法,从刚才创建的IQuoteService抽象类中扩展这个类(请连同这些文字一起查看代码)。

这里我们使用send方法向API发送请求:networkManager.send()。
send()期望我们提供各种参数。
其中一个是发送请求的路径(AppConstants.baseUrl)。
另一个是我们将使用的模型(QuoteModel),以及请求类型。
我们使用GET方法是因为我们将为这个应用程序接收数据。如果可以发送一个数据,我们将使用POST等。

好了,服务过程的最后一步是存储过程。

class QuotesRepository {
  final quoteService = QuoteService(VexanaManager());

  Future<QuoteModel> getAllQuotes() {
    return quoteService.getAllQuotes();
  }
}

在这里我们创建一个类。这个类将在后面的BLoC结构中使用。
一个提示:你可以把Repository类想象成Bloc和服务之间的一个层。
我们在最开始创建的VexanaManager是一个定义Vexana的类。

不多说了,让我们看看我们的Bloc结构。

我在这里的目的不是要深入研究Bloc的结构。我的目的是帮助那些已经有一些Bloc知识的人在高级水平上做到这一点。

abstract class QuoteEvent extends Equatable {
  const QuoteEvent();
}

class FetchQuotes extends QuoteEvent {
  
  List<Object> get props => [];
}

让我们先看一下我们的事件文件。在这个应用程序中只有一个事件,那就是从API中获取数据。提醒一下:事件是指用户的行为,状态是指他因这些行为而得到的状态。关键句子是:事件进入块结构,然后状态退出。

现在来检查一下状态文件。

abstract class QuoteState extends Equatable {
  const QuoteState();
}

class QuoteInitial extends QuoteState {
  
  List<Object?> get props => [];
}

class QuoteLoading extends QuoteState {
  
  List<Object> get props => [];
}

class QuoteLoaded extends QuoteState {
  final QuoteModel quoteModel;

  const QuoteLoaded(this.quoteModel);

  
  List<Object> get props => [quoteModel];
}

class QuoteError extends QuoteState {
  final String message;

  const QuoteError(this.message);

  
  List<Object> get props => [];
}

让我们想一想。我们的事件可能是什么?
首先,它可能处于初始状态。所以还没有什么。
然后用户按下按钮,想显示一个新的报价。
在这种情况下,从API获取数据的过程开始发挥作用,QuoteLoading()就从这里开始。我们从API中获取数据。
正如你所猜测的,这就是QuoteLoaded()发挥作用的地方。如果发生错误,则运行QuoteError状态。

提醒一下。在QuoteLoaded类中,我们定义了将从服务中获取的数据类型。我们将接收类型为QuoteModel的数据。

class QuoteBloc extends Bloc<QuoteEvent, QuoteState> {
  QuoteBloc() : super(QuoteInitial()) {
    final QuotesRepository quotesRepository = QuotesRepository();

    on<FetchQuotes>((event, emit) async {
      emit(QuoteLoading());
      try {
        var response = await quotesRepository.getAllQuotes();
        emit(QuoteLoaded(response));
      } catch (e) {
        emit(QuoteError(e.toString()));
      }
    });
  }
}

是的,由于上面所有的定义,你现在可以猜到这里发生了什么。
提醒一下:我们不要忘记在这里定义我们在服务操作中使用的资源库类 :)

然后我们把这个Bloc结构整合到接口编码中。

谢谢你的阅读。我希望它能对你有所帮助。保持健康!