Flutter riverpod обновляет состояние определенного индекса в ListView

У меня есть приложение, которое делает почтовый запрос к API с данными о драйверах и заказах. На начальной странице отображается список драйверов в отдельных плитках списка. На плитке списка есть раскрывающийся список. Нажав на эту опцию, вы попадете на новую страницу со списком заказов для этого драйвера. При нажатии на отдельный заказ открывается форма. При отправке и проверке этой формы я хочу изменить цвет текста заказов с красного на зеленый. Каждый заказ имеет отправленный флаг, и когда он отправляется, я хотел бы изменить его на true, а затем изменить цвет. Когда все заказы в представлении списка зеленые, я хочу, чтобы цвет этого драйвера стал зеленым. Я просматривал руководства и документацию по Riverpod, но не могу понять, как это сделать. Может ли кто-нибудь указать мне правильное направление?

main.dart

void main() {
  runApp(ProviderScope(child: MyApp()));
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      debugShowCheckedModeBanner: false,
      home: DriversPage(),
    );
  }
}

drivers.dart - здесь отображаются драйверы

class DriversPage extends StatelessWidget {
  final HttpService httpService = HttpService();
  var colorChoice = Colors.red;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: Color(0xFFFAFAFA),
      appBar: AppBar(
        title: Text("Drivers")
      ),
      body: Container(
        child: FutureBuilder(
        future: httpService.getOrders(),
        builder: (BuildContext context, AsyncSnapshot<List<Order>> snapshot) {
          if (snapshot.hasData) {
            List<Order> orders = snapshot.data;
            return ListView(
              children: orders.map((Order order) => Card(child: ExpansionTile(
                title: Text(order.driver, style: TextStyle(color: colorChoice),),
                children: <Widget>[
                  Container(
                    alignment: Alignment.center,
                    margin: EdgeInsets.all(10.0),
                    decoration: BoxDecoration(
                      color: Colors.white,
                      borderRadius: BorderRadius.circular(2.0),
                      border: Border.all(color: Colors.black26)
                    ),
                    child: ListTile(title: Text("Orders"),
                    trailing: Icon(Icons.keyboard_arrow_right),
                    onTap: () => Navigator.of(context).push(MaterialPageRoute(builder: (context) => OrdersState(driverName: order.driver, driverOrders: order.orders))),),
                  ),
                ],
              ))).toList(),
            );
          }
          return Center(child: CircularProgressIndicator());
        }),
    ));
  }
}

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

final driverListProvider = StateNotifierProvider((ref) => new DriverListTest());

class OrdersState extends ConsumerWidget {
  final String driverName;
  final List<OrderElement> driverOrders;

  const OrdersState({Key key, this.driverName, this.driverOrders}) : super(key: key);

  @override
  Widget build(BuildContext context, ScopedReader watch) {
    return Scaffold(
      appBar: AppBar(
        title: Text(driverName),
      ),
      body: ListView.builder(
          itemCount: driverOrders.length,
          itemBuilder: (context, index){
            final item = driverOrders[index];
            return Card(
                key: UniqueKey(),
                child: ListTile(title: Text(item.order, style: TextStyle(color: item.submitted? Colors.green : Colors.red),),
                subtitle: Text('${item.company}\n${item.address}'),
                onTap: () => Navigator.of(context).push(MaterialPageRoute(builder: (context) => OrderForm(orderTitle: item.order,))),));
          }),
      );
  }
}

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

class OrderForm extends StatefulWidget {
  final String orderTitle;

  const OrderForm({this.orderTitle});

  @override
  _OrderFormState createState() => _OrderFormState();
}

class _OrderFormState extends State<OrderForm> {
  @override
  final _formKey = GlobalKey<FormState>();
  final _orderModel = Order();
  List<String> _pickerNames = ['Loader 1', 'Loader 2', 'Loader 3', 'Loader 4'];
  String _selectedPickedBy;
  String _selectedCheckedBy;


  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(backgroundColor: Colors.blueGrey,title: Center(
          child: Text(widget.orderTitle),
        ),),
        floatingActionButton: FloatingActionButton(
          child: Icon(
              Icons.delete
          ),
          onPressed: () {
            showDialog(
                context: context,
            builder: (BuildContext context) {
                  return AlertDialog(
                    scrollable: true,
                    title: Text('Login'),
                    content: Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: Form(
                        child: Column(
                          children: <Widget>[
                            TextFormField(
                              decoration: InputDecoration(
                                labelText: 'Reason',
                                icon: Icon(Icons.account_box),
                              ),
                            ),
                            TextFormField(
                              decoration: InputDecoration(
                                labelText: 'Reason 1',
                                icon: Icon(Icons.email),
                              ),
                            ),
                            TextFormField(
                              decoration: InputDecoration(
                                labelText: 'Reason 2',
                                icon: Icon(Icons.message),
                              ),
                            ),
                          ],
                        ),
                      ),
                    ),
                    actions: [
                      RaisedButton(
                          child: Text("Submit"),
                          onPressed: () {

                          })
                    ],
                  );
            });
          }
        ),
        body: Container(
          padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0),
          child: Builder(
            builder: (context) => Form(
                key: _formKey,
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.stretch,
                  children: [
                    DropdownButtonFormField(
                      isExpanded: true,
                      hint: Text('Picked By'),
                      value: _selectedPickedBy,
                      onChanged: (newValue){
                        setState(() {
                          _selectedPickedBy = newValue;
                        });
                      },
                      validator: (value) => value == null
                          ? 'Picked By Required' : null,
                      items: _pickerNames.map((picker) {
                        return DropdownMenuItem(
                          child: new Text(picker),
                          value: picker,
                        );
                      }).toList(),
                      onSaved: (value) => setState(() => _orderModel.pickedBy = value) ,
                    ),
                    Container(
                      padding: const EdgeInsets.symmetric(
                          vertical: 16.0, horizontal: 16.0
                      ),
                      child: RaisedButton(
                        onPressed: (){
                          final form = _formKey.currentState;
                          if (form.validate()){
                            form.save();
                            Navigator.pop(context,);
                          }
                        },
                        child: Text("Submit"),
                      ),
                    )
                  ],
                )),
          ),
        )
    );

  }
}

ordermodel.dart - это модель для драйверов и заказов при выполнении HTTP-запросов к моему API. Внизу вы можете увидеть, где я пытаюсь создать statenotifier и что я пытаюсь сделать, принимая список OrderElement (список заказов).

List<Order> orderFromJson(String str) => List<Order>.from(json.decode(str).map((x) => Order.fromJson(x)));

String orderToJson(List<Order> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));

class Order {
  Order({
    this.driver,
    this.orders,
  });

  String driver;
  List<OrderElement> orders;

  factory Order.fromJson(Map<String, dynamic> json) => Order(
    driver: json["Driver"],
    orders: List<OrderElement>.from(json["Orders"].map((x) => OrderElement.fromJson(x))),
  );

  Map<String, dynamic> toJson() => {
    "Driver": driver,
    "Orders": List<dynamic>.from(orders.map((x) => x.toJson())),
  };
}

class OrderElement {
  OrderElement({
    this.order,
    this.company,
    this.address,
    this.submitted,
    this.index,
  });

  String order;
  String company;
  String address;
  bool submitted;
  num index;

  factory OrderElement.fromJson(Map<String, dynamic> json) => OrderElement(
    order: json["Order"],
    company: json["Company"],
    address: json["Address"],
    submitted: json["submitted"],
    index: json["index"]
  );

  Map<String, dynamic> toJson() => {
    "Order": order,
    "Company": company,
    "Address": address,
  };
}

class DriverListTest extends StateNotifier<List<OrderElement>> {
  DriverListTest([List<OrderElement> drivers1]) : super(drivers1 ?? []);


  void onSubmit(num index) {
    state = [
      for(final currentOrder in state)
        if (currentOrder.index == index)
          OrderElement(
            order: currentOrder.order,
            company: currentOrder.company,
            address: currentOrder.address,
            submitted: !currentOrder.submitted,
            index: currentOrder.index,
          )
      else
        currentOrder,
    ];
  }
}

Не знаю, нужен ли мой класс Http, но дайте мне знать, если это так. Я пробовал следовать https://www.refactord.com/guides/riverpod-state-management-explained и Как установить состояние виджета по индексу в listview.builder в flutter как обрабатывать отдельные виджеты, но опять же я просто заблудился. Любая помощь будет принята с благодарностью! Заранее спасибо.


person lazydev    schedule 05.03.2021    source источник