Можно ли добиться управления состоянием между двумя виджетами без состояния без диспетчера состояний?

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

Примечание с предварительным условием. Я знакомлюсь с использованием GetX в основном для управления состоянием, поэтому я хотел бы попытаться всегда использовать виджеты без сохранения состояния. Пожалуйста, имейте это в виду в своем ответе. Я знаю, что могу решить эту проблему с помощью виджета с состоянием setState(). Я также знаю, что могу решить эту проблему с помощью класса контроллера, расширяющего GetXController() или даже провайдера. Однако мне просто любопытно, возможно ли то, что я пытаюсь сделать, без этих очевидных решений...

Итак... Вопрос:

Возможно ли, чтобы rangeStart и rangeEnd этого виджета (при изменении пользователем выбора даты) перестраивали текстовый виджет в следующем виджете ниже?

import 'package:flutter/material.dart';
import 'package:date_range_picker/date_range_picker.dart' as DateRangePicker;

// ignore: must_be_immutable
class DateRange extends StatelessWidget {
  DateTime rangeStart = DateTime.now().subtract(Duration(days: 7));
  DateTime rangeEnd = DateTime.now();

  @override
  Widget build(BuildContext context) {
    return IconButton(
        color: Colors.white,
        icon: Icon(Icons.calendar_today),
        onPressed: () async {
          final List<DateTime> picked = await DateRangePicker.showDatePicker(
              context: context,
              initialFirstDate: DateTime.now().subtract(Duration(days: 7)),
              initialLastDate: DateTime.now(),
              firstDate: DateTime(2015),
              lastDate: DateTime(DateTime.now().year + 2));
          if (picked != null && picked.length == 2) {
            rangeStart = picked.first;
            rangeEnd = picked.last;
          }
        });
  }
}
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:linechart/add_data_button.dart';
import 'package:linechart/date_range.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
import 'package:intl/intl.dart';

class LineChartScreen extends StatelessWidget {
  final DateRange dateRange = Get.put(DateRange());

  @override
  Widget build(BuildContext context) {
    String startDate = DateFormat.yMMMMd('en_US').format(dateRange.rangeStart);
    String endDate = DateFormat.yMMMMd('en_US').format(dateRange.rangeEnd);

    return Scaffold(
      appBar: AppBar(
        title: Text('GRAPH --- Range: $startDate - $endDate'),
        backgroundColor: Colors.lightBlue,
        actions: [
          AddDataButton(),
          DateRange(),
        ],
      ),
      body: Column(...),
    );
  }
}

person RobbB    schedule 07.05.2021    source источник


Ответы (1)


Этот старый пост объясняет, что:

StatelessWidget никогда не будет перестраиваться сам по себе (но может из-за внешних событий). StatefulWidget может. Это золотое правило.

Итак, вам нужен какой-то механизм, чтобы заставить StatelessWidget перестроиться, и, как вы говорите, их много.

Мне нравится использовать flutter_hooks как простой механизм управления таким приватным состоянием. Вот код для вашего примера, который его использует. В коде:

  • Родительский виджет вызывает useState для определения/доступа к своему состоянию
  • Дочерний виджет вызывает обратный вызов для обновления состояния, что приводит к перестройке родителя.

Код:

import 'package:flutter/material.dart';
import 'package:date_range_picker/date_range_picker.dart' as DateRangePicker;
import 'package:intl/intl.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

// ignore: must_be_immutable
class DateRangeWidget extends StatelessWidget {
  Function _setDateRange;

  DateRangeWidget(this._setDateRange);

  DateTime rangeStart = DateTime.now().subtract(Duration(days: 7));
  DateTime rangeEnd = DateTime.now();

  @override
  Widget build(BuildContext context) {
    return IconButton(
        color: Colors.white,
        icon: Icon(Icons.calendar_today),
        onPressed: () async {
          final List<DateTime> picked = await DateRangePicker.showDatePicker(
              context: context,
              initialFirstDate: DateTime.now().subtract(Duration(days: 7)),
              initialLastDate: DateTime.now(),
              firstDate: DateTime(2015),
              lastDate: DateTime(DateTime.now().year + 2));
          if (picked != null && picked.length == 2) {
            rangeStart = picked.first;
            rangeEnd = picked.last;
            _setDateRange(rangeStart, rangeEnd);
          }
        });
  }
}

class DateRange {
  DateTime rangeStart, rangeEnd;
  DateRange(this.rangeStart, this.rangeEnd);
}

class LineChartScreen extends HookWidget {
//  final DateRange dateRange = Get.put(DateRange());
  @override
  Widget build(BuildContext context) {
    final dateRange = useState(
        DateRange(DateTime.now().subtract(Duration(days: 7)), DateTime.now()));

    void _setDateRange(rangeStart, rangeEnd) {
      dateRange.value = DateRange(rangeStart, rangeEnd);
    }

    String startDate =
        DateFormat.yMMMMd('en_US').format(dateRange.value.rangeStart);
    String endDate =
        DateFormat.yMMMMd('en_US').format(dateRange.value.rangeEnd);

    return Scaffold(
      appBar: AppBar(
        title: Text('GRAPH --- Range: $startDate - $endDate'),
        backgroundColor: Colors.lightBlue,
        actions: [
          // AddDataButton(),
          DateRangeWidget(_setDateRange),
        ],
      ),
      body: Column(
        children: [
          Text(startDate),
          Text(endDate),
        ],
      ),
    );
  }
}
person Patrick O'Hara    schedule 07.05.2021
comment
Красиво и просто, я посмотрю на крючки. Спасибо за помощь :) - person RobbB; 08.05.2021