Использование пакета провайдера StreamProvider с декларативным интерфейсом - PullRequest
0 голосов
/ 05 июня 2019

Я пытался обернуться вокруг потока данных и управления состоянием во флаттере, особенно используя пакет провайдера.Я попытался сделать запрос из firestore, используя Future, но оказалось, что он не полностью использует возможности firestore, поскольку Future s ожидает только одно значение.Поэтому я переключился на использование Stream s вместо StreamProvider, чтобы получить данные для моих виджетов.Проблема в том, что я не могу понять, в какой момент / как сказать пользовательскому интерфейсу для восстановления.Иногда в потоке есть данные, иногда - нет, и я не могу точно сказать, что происходит в те времена.Скажем, я хотел показать круговой индикатор выполнения, когда представление занято, как мне добиться этого при использовании StreamProvider.Имеет ли смысл даже это делать?

Так что с подходом заполненные стеки можно переключать состояние модели между занятым и незанятым при выполнении сетевого вызова с использованием фьючерсов.Например, когда мы вызываем метод getPosts, мы сначала устанавливаем состояние Busy, чтобы пользовательский интерфейс мог реагировать и, возможно, отображать индикатор загрузки.Однако мы можем установить состояние обратно в режим ожидания после получения асинхронных данных.A Stream, с другой стороны, по крайней мере, насколько я понимаю, это просто канал, который постоянно передает данные, и я не могу придумать способ достижения состояния Busy / Idle или даже сказать, есть ли у этого Streamновые данные / любые данные.Есть идеи?

Это часть моего database_service.dart, которая должна возвращать поток данных из firestore.

Stream<List<Expense>> getExpenses(int month, int year) {
    var ref = _db
        .collection("userData")
        .document(fireUser.uid)
        .collection("expenses")
        .where("month", isEqualTo: month)
        .where("year", isEqualTo: year)
        .orderBy("date", descending: true);

    return ref.snapshots().map((list) =>
        list.documents.map((snap) => Expense.fromFirestore(snap)).toList());
  }

Это часть моего expenses.dart представления файла, который использует StreamProvider.

class ExpensesPage extends StatefulWidget {
  @override
  _ExpensesPageState createState() => _ExpensesPageState();
}
class _ExpensesPageState extends State<ExpensesPage> {
  int _month;
  int _year;

  @override
  void initState() {
    super.initState();
    _month = DateTime.now().month;
    _year = DateTime.now().year;
  }

    @override
  Widget build(BuildContext context) {
    return StreamProvider<List<Expense>>.value(
      stream: locator<ExpensesProvider>().getMonthExpenses(_month, _year),
      initialData: [],
      child: StreamProvider<List<Category>>.value(
        stream: locator<ExpensesProvider>().getCategories(),
        initialData: [],
        child: Scaffold(
          body: Container(
            alignment: Alignment.center,
            color: Theme.of(context).primaryColor,
            padding: EdgeInsets.all(10.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                InkWell(
                  child: DateHeader("$_month $_year"),
                  onTap: () => _selectDate(),
                ),
                Expanded(
                  child: ExpenseList(
                    month: _month,
                    year: _year,
                  ),
                )
              ],
            ),
          ),
        ),
      ),
    );
  }

и expense_list.dart с виджетом ExpenseList, который потребляет данные из StreamProvider.

class ExpenseList extends StatelessWidget {
  final _dateFormat = DateFormat.yMMMEd();
  final _dbService = locator<DatabaseService>();
  final int month;
  final int year;

  ExpenseList({@required this.month, @required this.year});

  String getMonthName(int month) {
    return _dbService.monthNames[month - 1];
  }

  @override
  Widget build(BuildContext context) {
    var expenses = Provider.of<List<Expense>>(context);
    var categories = Provider.of<List<Category>>(context);
    return expenses.length < 1
        ? Center(
            child: Padding(
              padding: const EdgeInsets.all(10.0),
              child: Text(
                "You do not have any expenses for ${getMonthName(month)}, $year",
                style: Theme.of(context).textTheme.subhead,
                textAlign: TextAlign.center,
              ),
            ),
          )
        : ListView.builder(
            itemBuilder: (BuildContext context, int index) {
              var cat = categories.firstWhere(
                (category) => category.id == expenses[index].category,
                orElse: () => Category.initial(),
              );
              var sub = cat.subs[expenses[index].subcategory];
              var amount = expenses[index].amount;
              var date = expenses[index].date;
              return ExpenseCard(
                  expenseCategory: cat.name,
                  expenseSubCategory: sub,
                  expenseAmount: amount,
                  expenseDate: _dateFormat.format(date),
                  cardColor: Theme.of(context).cardColor);
            },
            itemCount: expenses.length,
          );
  }
}

Мой пользовательский интерфейс постоянно на этом

stage

, хотя я ожидаю, что он обновится с данными из StreamProvider.

...