Как использовать два и более провайдера для одного виджета, в одном и том же месте кода?

Как я мог бы использовать два и более провайдера для изменения состояния виджета?

К примеру, у меня есть мобильное приложение (магазин одежды). В нем есть категории товаров. Как мне для товаров, находящихся в разных категориях использовать один и тот же код, присваивающий товару, при нажатии на товар: название, изображение, описание.
Надеюсь, что смог грамотно объяснить свою проблему. Ниже оставлю код для присваивания товару названия и изображения. Комментариями отмечены места в коде, где я хочу использовать вариативность. Я думаю, что это можно реализовать, если приложение будет проверять состояние экрана. То есть: когда пользователь свайпает на экран с одним товаром, приложение детектит экран и присваивает товарам название, описание и тд., те которые должны быть на данном экране. Точно по такому же принципу происходило бы присваивание для товаров в других категориях, на других экранах.

import 'package:flutter/material.dart';
import 'package:lvmarketapn/providers/women/wbags_data_provider.dart';
import 'package:provider/provider.dart';
import '../../constants.dart';

// ignore: must_be_immutable
class ProductTitleWithImage extends StatelessWidget {
  final String wBagId;
  final String wJewellId;

  ProductTitleWithImage({Key key, this.wBagId, this.wJewellId})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    final wBags = Provider.of<WBags>(context).getWbag(wBagId);
    final wJewells = Provider.of<WJewellery>(context).getWjewell(wJewellId); //!!!

    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: lDP, vertical: lDP),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Container(
            margin: const EdgeInsets.only(top: 10.0),
            child: Text(
              // I want it shows this provider values when user swipe to special screen
              wBags.title,
              style: Theme.of(context)
                  .textTheme
                  .headline6
                  .copyWith(fontWeight: FontWeight.bold, color: lTC),

                  //!!!
                  // And I want it shows another provider values when user swipe to another special page
                  // For example :
                  // wJewells.title,
                  // style: Theme.of(context)
                  //     .textTheme
                  //     .headline6
                  //     .copyWith(fontWeight: FontWeight.bold, color: lTC),
                ),
              ),
              SizedBox(height: lDP),
              Row(
                children: <Widget>[
                  RichText(
                    text: TextSpan(
                      children: [
                        TextSpan(
                          text: 'Price\n',
                          style: Theme.of(context)
                              .textTheme
                              .headline6
                              .copyWith(fontWeight: FontWeight.bold, color: lTC),
                        ),
                        TextSpan(
                          // I want it shows this provider values when user swipe to special screen
                          text: '\$${wBags.price}',
                          style: Theme.of(context)
                              .textTheme
                              .headline6
                              .copyWith(fontWeight: FontWeight.bold, color: lTC),
    
                          //!!!
                          // And I want it shows another provider values when user swipe to another special //page
                          // For example :
                          /* text: '\$${wJewells.price}',
                          style: Theme.of(context)
                              .textTheme
                              .headline6
                              .copyWith(fontWeight: FontWeight.bold, color: lTC),*/
                        )
                      ],
                    ),
                  ),
                  SizedBox(
                    width: lDP,
                  ),
                  Expanded(
                    child: Hero(
                      // I want it shows this provider values when user swipe to special screen
                      tag: '${wBags.id}',
                      child: Image.asset(
                        wBags.image,
                        fit: BoxFit.cover,
    
                        //!!!
                        // And I want it shows another provider values when user swipe to another special //page
                        // For example :
    //                   tag: '${wJewells.id}',
    //                   child: Image.asset(
    //                     wBags.image,
    //                     fit: BoxFit.cover,
                      ),
                    ),
                  ),
                ],
              ),
            ],
          ),
        );
      }
    }
      

Ответы (2 шт):

Автор решения: MiT

Что тут происходит:

  1. Я сделал абстрактный класс, с общими полями и методами
  2. Реализовал его в WBags и WJewellery
  3. Через Generic пропихнул его в виджет
  4. И использовал его общие поля и методы
  5. При регистрации в provider, я использовал реализованные WBags и WJewellery
  6. При использовании в виджете я явно указываю нужный мне тип (на данном этапе вы знаете какой вам нужен тип, по этому сможете его подставить)

Примерно так будет:

abstract class WProduct {
  const WProduct(this.id, this.title, this.price, this.image);
  final String id;
  final String title;
  final String price;
  final String image;
    
  void getById(String id);
}

class WBags extends WProduct {
  const WBags(String id, String title, String price, String image) : super(id, title, price, image);
  
  @override
  void getById(String id){
    // ...
  }
}

class WJewellery extends WProduct {
  const WJewellery(String id, String title, String price, String image) : super(id, title, price, image);
  
  @override
  void getById(String id){
    // ...
  }
}

Виджет:

class ProductTitleWithImage<T extends WProduct> extends StatelessWidget {
  final String id;

  ProductTitleWithImage({Key key, this.id}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final WProduct wProduct = Provider.of<T>(context).getById(id);

    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: lDP, vertical: lDP),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Container(
            margin: const EdgeInsets.only(top: 10.0),
            child: Text(
              wProduct.title,
              style: Theme.of(context)
                  .textTheme
                  .headline6
                  .copyWith(fontWeight: FontWeight.bold, color: lTC),
            ),
          ),
          SizedBox(height: lDP),
          Row(
            children: <Widget>[
              RichText(
                text: TextSpan(
                  children: [
                    TextSpan(
                      text: 'Price\n',
                      style: Theme.of(context)
                          .textTheme
                          .headline6
                          .copyWith(fontWeight: FontWeight.bold, color: lTC),
                    ),
                    TextSpan(
                      text: '\$${wProduct.price}',
                      style: Theme.of(context)
                          .textTheme
                          .headline6
                          .copyWith(fontWeight: FontWeight.bold, color: lTC),
                    )
                  ],
                ),
              ),
              SizedBox(
                width: lDP,
              ),
              Expanded(
                child: Hero(
                  tag: '${wProduct.id}',
                  child: Image.asset(
                    wProduct.image,
                    fit: BoxFit.cover,
                  ),
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

Регистрация в provider:

Provider(
  create: (_) => WBags(),
  child: ...
)
...
Provider(
  create: (_) => WJewellery(),
  child: ...
)

Использование:

ProductTitleWithImage<WBags>(id:...)
...
ProductTitleWithImage<WJewellery>(id:...)
→ Ссылка
Автор решения: Vadim Popov

Думаю стоит использовать класс MultiProvider

Пример:

MultiProvider(
  providers: [
    Provider<Something>(create: (_) => Something()),
    Provider<SomethingElse>(create: (_) => SomethingElse()),
    Provider<AnotherThing>(create: (_) => AnotherThing()),
  ],
  child: someWidget,
)
→ Ссылка