Riverpod - Как обернуть виджет PreferredSize внутри потребителя

У меня есть DefaultTabController, и у меня есть метод, который возвращает List<PreferredSizeWidget>, которые являются AppBar страниц. Я хочу, чтобы они наблюдали за состоянием в ChangeNotifier, и поэтому я хочу заключить их в Consumer. Когда я пытаюсь это сделать, я получаю такую ​​ошибку:

Тип аргумента Widget не может быть назначен типу параметра PreferredSizeWidget.

Как я могу это исправить?

Спасибо и привет.


person Burak Akyalçın    schedule 13.12.2020    source источник


Ответы (2)


Ошибка возникает из-за параметра appBar Scaffold, требующего PreferredSizeWidget. Я могу придумать два решения:

  • Вы можете заключить Consumer в PreferredSize и использовать Size.fromHeight() как preferredSize. То есть, если высота среди ваших панелей приложений постоянна.
  • Вы можете полностью избежать использования параметра appBar, заключив тело Scaffold в Expanded внутри Column и сделав Consumer его первым дочерним элементом.
person MickaelHrndz    schedule 13.12.2020
comment
спасибо, я приму этот ответ. - person Burak Akyalçın; 14.12.2020
comment
Пожалуйста :) - person MickaelHrndz; 14.12.2020

Это моя реализация, основанная на предложении Микаэля:

Сначала я создал AppBarParams класс для сохранения состояния AppBar.

@freezed
class AppBarParams with _$AppBarParams {
  const factory AppBarParams({
    required String title,
    required List<Widget> actions,
  }) = _AppBarParams;
}

Затем я создал StateProvider в файле глобальных поставщиков, например:

final appBarParamsProvider = StateProvider<AppBarParams>((ref) {
  return AppBarParams(title: "Default title", actions: []);
});

и прикрепил его к основному приложению с помощью Consumer:

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: "App Title",
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: SafeArea(
        child: Scaffold(
          appBar: PreferredSize(
            preferredSize: Size.fromHeight(kToolbarHeight),
            child: Consumer(
              builder: (context, watch, child) {
                final appBarParams = watch(appBarParamsProvider).state;
                return AppBar(
                  title: Text(appBarParams.title),
                  actions: appBarParams.actions
                );
            })
          ),
          body: ... your body widget
        )
      )
    )
  }
}

Затем вам просто нужно отредактировать состояние поставщика, чтобы обновить AppBar соответствующим образом, чтобы обновить AppBar, когда пользователь переключается между страницами, я создал этот миксин:

mixin AppBarHandler {

  void updateAppBarParams(
    BuildContext context, {
    required String title,
    List<Widget> Function()? actions
  }) {
    WidgetsBinding.instance!.addPostFrameCallback((_) async {
      context.read(appBarParamsProvider).state = context
        .read(appBarParamsProvider).state
        .copyWith(
           title: title, 
           actions: actions != null ? actions() : []
        );
    });
  }
}

И на каждом главном экране, который должен изменить заголовок или действия, которые я сделал:

class Page1 extends HookWidget with AppBarHandler {

  const Page1({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    updateAppBarParams(context, 
      title: "Page 1 title",
      actions: () => [
        IconButton(icon: const Icon(Icons.refresh), onPressed: () {
          //a custom action for Page1
          context.read(provider.notifier).updateEntries();
        })
      ]
    );
    ... your screen widget
  }
}
person MatPag    schedule 11.04.2021