Как пользоваться библиотекой блоков?

#flutter #dart

Вопрос:

Я пытаюсь разобраться в библиотеке блоков, но у меня от этого болит голова.

Я пытаюсь получить названия отелей из API. У меня есть модель и служба, ответственная за обращение к API и получение данных. Однако я не знаю, как подключить его к библиотеке блоков.

Как только мое приложение запустится, я хочу, чтобы блок извлек данные из API, а затем показал их в приложении.

Вот мой код:

hotel_model.dart

 class Hotels {
  final List<Hotel> hotels;

  Hotels({this.hotels});

  factory Hotels.fromJson(Map<String, dynamic> json) {
    return Hotels(
      hotels: List<Hotel>.from(
        json['hotels'].map(
          (x) => Hotel.fromJson(x),
        ),
      ),
    );
  }
}

class Hotel {
  final String hotelName;

  Hotel({this.hotelName});

  factory Hotel.fromJson(Map<String, dynamic> json) {
    return Hotel(
      hotelName: json['name'],
    );
  }
}

 

hotel_service.дарт

 import 'package:http/http.dart' as http;

abstract class DownloadService {
  Future<http.Response> fetchHotels();
}

class HotelService extends DownloadService {
  @override
  Future<http.Response> fetchHotels() {
    final Uri uri = Uri.https('services.lastminute.com', 'mobile/stubs/hotels');

    return http.get(uri);
  }
}

 

И вот что я сделал с блоком lib.

hotel_event.дарт

 part of 'hotel_bloc.dart';

@immutable
abstract class HotelEvent {}

class OnAppStartEvent extends HotelEvent {}

 

hotel_bloc.дарт

 import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:hotels/models/hotel/hotel_model.dart';
import 'package:hotels/services/hotel/hotel_service.dart';
import 'package:meta/meta.dart';

part 'hotel_event.dart';
part 'hotel_state.dart';

class HotelBloc extends Bloc<HotelEvent, HotelState> {
  HotelBloc() : super(HotelFinal());

  final HotelService hotelService = HotelService();

  @override
  Stream<HotelState> mapEventToState(
    HotelEvent event,
  ) async* {
    if (event is FetchEvent) {
      final response = hotelService.fetchHotels();

      yield 
    }
  }
}

 

hotel_state.дарт

 part of 'hotel_bloc.dart';

@immutable
abstract class HotelState {
  HotelState();
}

class HotelFinal extends HotelState {
  final Hotel hotel;
  HotelFinal(this.hotel);

  Hotel getHotel() {
    return hotel;
  }
}

 

Комментарии:

1. Взгляните на библиотеку блоков , чтобы понять основы

2. @quoci Да, я знаю основы, но там много устаревшей информации.

3. Поставьте ожидание в hotelService. fetchHotels(); После этого у вас будут все ваши отели. теперь вы хотите вывести эти данные в состояние, например, ваш HotelFinal -> вывести HotelFinal(ответ)

4. Я рекомендую вам этот пакет . Это довольно удобно, если вы используете блоки.

Ответ №1:

  1. Прежде всего добавьте await в эту строку в своем блоке
    final response = await hotelService.fetchHotels();
  2. возврат List<Hotel> из вашей fetchHotels функции
  3. у вас должен быть stateful класс для вашего экрана, и в initState нем вы можете создать свой объект блока и вызвать .add метод на нем
  4. в своем build методе оберните виджет с BlocBuilder помощью обратного вызова и при builder обратном вызове проверьте состояние вашего блока, если состояние HotelFinal соответствует возвращению пользовательского интерфейса со списком отелей в объекте состояния.

Ответ №2:

  1. Будет полезно добавить другое состояние для вашего HotelState , когда ваш блок извлекает данные, и даже когда возникает ошибка. например;
 part of 'hotel_bloc.dart';

@immutable
abstract class HotelState {
  HotelState();
}

class HotelFinal extends HotelState {
  final Hotel hotel;
  HotelFinal(this.hotel);

  Hotel getHotel() {
    return hotel;
  }
}

class HotelLoading extends HotelState {
  HotelLoading();
}

class HotelError extends HotelState {
  final String error;
  HotelError(this.error);
}
 
  1. Вы хотели бы изменить свой mapEventToState на что-то вроде этого:
  @override
  Stream<HotelState> mapEventToState(
    HotelEvent event,
  ) async* {
    if (event is FetchEvent) {
      yield HotelLoading();
      try {
        final response = await hotelService.fetchHotels();
        // It seems like your service doesn't return an hotel directly, so you'll have to deal with this as it is not part of the question.
        final hotel = getYourHotelHereWithTheResponse;
        yield HotelFinal(hotel);
      } catch (e) {
        yield HotelError('something went wrong getting the hotel info');
      }
    }
   }

 
  1. Наконец, добавьте виджет в дерево FetchEvent виджетов, который добавляет к вашему блоку, и добавьте a BlocBuilder , чтобы реагировать на изменение состояний. Обратите внимание, что это очень гибко и может быть сделано многими способами, но это выходит за рамки вашего очень широкого вопроса, я просто показываю вам, как использовать библиотеку минимально:
 
class MyStatefulWidget extends StatefulWidget {
  @override
  _MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {

  HotelBloc hotelBloc;

  @override
  void initState() {
    hotelBloc = HotelBloc..add(FetchEvent());
    super.initState();
  }

  @override
  void dispose() {
    hotelBloc.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return BlocBuilder(builder: (context, state) {
      if(state is HotelLoading) {
        // return a widget to deal with loading
      }
      
      if(state is HotelFinal) {
        // return a widget to deal with success
      }
      
      if(state is HotelError) {
        // return a widget to deal with error
      }
    });
  }
}