diff --git a/packages/dart_flutter_common/lib/src/widgets/section.dart b/packages/dart_flutter_common/lib/src/widgets/section.dart index ee4807e9..682d8658 100644 --- a/packages/dart_flutter_common/lib/src/widgets/section.dart +++ b/packages/dart_flutter_common/lib/src/widgets/section.dart @@ -9,6 +9,7 @@ class Section extends StatelessWidget { /// [Section] を作成する。 const Section({ required this.title, + this.titleBadge, this.titleStyle, this.titleMaxLines = 1, this.titleBottomMargin = 16, @@ -24,6 +25,9 @@ class Section extends StatelessWidget { /// セクションのタイトル。 final String title; + /// タイトルの横に配置されるバッジ + final Widget? titleBadge; + /// [title] のスタイル。 final TextStyle? titleStyle; @@ -58,10 +62,15 @@ class Section extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - title, - style: titleStyle, - maxLines: titleMaxLines, + Row( + children: [ + Text( + title, + style: titleStyle, + maxLines: titleMaxLines, + ), + if (titleBadge != null) titleBadge!, + ], ), SizedBox(height: titleBottomMargin), if (description != null) ...[ diff --git a/packages/dart_flutter_common/lib/src/widgets/selectable_chips.dart b/packages/dart_flutter_common/lib/src/widgets/selectable_chips.dart index f08c173b..b2877cc8 100644 --- a/packages/dart_flutter_common/lib/src/widgets/selectable_chips.dart +++ b/packages/dart_flutter_common/lib/src/widgets/selectable_chips.dart @@ -17,6 +17,7 @@ class SelectableChips extends StatelessWidget { this.spacing = 8, this.runSpacing = 0, this.crossAxisAlignment = WrapCrossAlignment.start, + this.onTap, this.chipPadding = const EdgeInsets.all(8), super.key, }); @@ -48,6 +49,9 @@ class SelectableChips extends StatelessWidget { /// [direction] と垂直方向の並び方。 final WrapCrossAlignment crossAxisAlignment; + /// チップが押されたときのコールバック + final void Function(T item)? onTap; + /// チップの余白。 final EdgeInsetsGeometry chipPadding; @@ -68,6 +72,7 @@ class SelectableChips extends StatelessWidget { label: labels[item] ?? '', isEnabled: enabledItems.contains(item), padding: chipPadding, + onTap: () => onTap?.call(item), ), ) .toList(), @@ -80,27 +85,32 @@ class _SwitchingChip extends StatelessWidget { required this.label, required this.padding, this.isEnabled = false, + this.onTap, }); final String label; final EdgeInsetsGeometry padding; final bool isEnabled; + final void Function()? onTap; @override Widget build(BuildContext context) { - return InputChip( - onPressed: () => {}, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - isEnabled: isEnabled, - side: isEnabled ? BorderSide.none : null, - label: Text( - label, - style: TextStyle( - color: Theme.of(context).colorScheme.onSecondaryContainer, + return GestureDetector( + onTap: onTap, + child: InputChip( + onPressed: onTap, // コールバックを設定しないとデザインが変わるので、設定している + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + isEnabled: isEnabled, + side: isEnabled ? BorderSide.none : null, + label: Text( + label, + style: TextStyle( + color: Theme.of(context).colorScheme.onSecondaryContainer, + ), ), + padding: padding, + backgroundColor: Theme.of(context).colorScheme.secondaryContainer, ), - padding: padding, - backgroundColor: Theme.of(context).colorScheme.secondaryContainer, ); } } diff --git a/packages/firebase_common/lib/src/firestore_repositories/job.dart b/packages/firebase_common/lib/src/firestore_repositories/job.dart index 03e3abdf..8139e0d2 100644 --- a/packages/firebase_common/lib/src/firestore_repositories/job.dart +++ b/packages/firebase_common/lib/src/firestore_repositories/job.dart @@ -22,4 +22,60 @@ class JobRepository { _query.subscribeDocuments( queryBuilder: (query) => query.where('hostId', isEqualTo: hostId), ); + + /// [Job] の情報を作成する。 + Future create({ + required String hostId, + required String title, + required String content, + required String place, + required Set accessTypes, + required String accessDescription, + required String belongings, + required String reward, + required String comment, + required String imageUrl, + }) => + _query.add( + createJob: CreateJob( + hostId: hostId, + title: title, + content: content, + place: place, + accessTypes: accessTypes, + accessDescription: accessDescription, + belongings: belongings, + reward: reward, + comment: comment, + imageUrl: imageUrl, + ), + ); + + /// [Job] の情報を更新する。 + Future update({ + required String jobId, + String? title, + String? content, + String? place, + Set? accessTypes, + String? accessDescription, + String? belongings, + String? reward, + String? comment, + String? imageUrl, + }) => + _query.update( + jobId: jobId, + updateJob: UpdateJob( + title: title, + content: content, + place: place, + accessTypes: accessTypes, + accessDescription: accessDescription, + belongings: belongings, + reward: reward, + comment: comment, + imageUrl: imageUrl, + ), + ); } diff --git a/packages/mottai_flutter_app/lib/development/development_items/ui/development_items.dart b/packages/mottai_flutter_app/lib/development/development_items/ui/development_items.dart index 5fa1ccd6..d73cd0ec 100644 --- a/packages/mottai_flutter_app/lib/development/development_items/ui/development_items.dart +++ b/packages/mottai_flutter_app/lib/development/development_items/ui/development_items.dart @@ -8,7 +8,9 @@ import '../../../chat/ui/chat_room.dart'; import '../../../chat/ui/chat_rooms.dart'; import '../../../host/ui/create_or_update_host.dart'; import '../../../host/ui/host.dart'; +import '../../../job/ui/job_create.dart'; import '../../../job/ui/job_detail.dart'; +import '../../../job/ui/job_update.dart'; import '../../../map/ui/map.dart'; import '../../../worker/ui/worker.dart'; import '../../color/ui/color.dart'; @@ -60,6 +62,16 @@ class DevelopmentItemsPage extends ConsumerWidget { JobDetailPage.location(jobId: 'PYRsrMSOApEgZ6lzMuUK'), ), ), + ListTile( + title: const Text('仕事情報作成ページ'), + onTap: () => context.router.pushNamed(JobCreatePage.location), + ), + ListTile( + title: const Text('仕事情報編集ページ'), + onTap: () => context.router.pushNamed( + JobUpdatePage.location(jobId: 'PYRsrMSOApEgZ6lzMuUK'), + ), + ), ListTile( title: const Text('チャットルーム一覧ページ(StreamProvider、未既読管理)'), onTap: () => context.router.pushNamed(ChatRoomsPage.location), diff --git a/packages/mottai_flutter_app/lib/job/job.dart b/packages/mottai_flutter_app/lib/job/job.dart index 856955a2..abe5b85b 100644 --- a/packages/mottai_flutter_app/lib/job/job.dart +++ b/packages/mottai_flutter_app/lib/job/job.dart @@ -54,6 +54,58 @@ class JobService { Stream subscribeJob({required String jobId}) => _jobRepository.subscribeJob(jobId: jobId); + /// [Job] の情報を作成する。 + Future create({ + required String hostId, + required String title, + required String content, + required String place, + required Set accessTypes, + required String accessDescription, + required String belongings, + required String reward, + required String comment, + required String imageUrl, + }) => + _jobRepository.create( + hostId: hostId, + title: title, + content: content, + place: place, + accessTypes: accessTypes, + accessDescription: accessDescription, + belongings: belongings, + reward: reward, + comment: comment, + imageUrl: imageUrl, + ); + + /// [Job] の情報を更新する。 + Future update({ + required String jobId, + String? title, + String? content, + String? place, + Set? accessTypes, + String? accessDescription, + String? belongings, + String? reward, + String? comment, + String? imageUrl, + }) => + _jobRepository.update( + jobId: jobId, + title: title, + content: content, + place: place, + accessTypes: accessTypes, + accessDescription: accessDescription, + belongings: belongings, + reward: reward, + comment: comment, + imageUrl: imageUrl, + ); + /// 指定したユーザーの [Job] を全件取得する。 Future> fetchUserJobs({required String hostId}) => _jobRepository.fetchUserJobs(hostId: hostId); diff --git a/packages/mottai_flutter_app/lib/job/ui/job_controller.dart b/packages/mottai_flutter_app/lib/job/ui/job_controller.dart new file mode 100644 index 00000000..7c3e3e35 --- /dev/null +++ b/packages/mottai_flutter_app/lib/job/ui/job_controller.dart @@ -0,0 +1,103 @@ +import 'dart:io'; + +import 'package:firebase_common/firebase_common.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +import '../../image/firebase_storage.dart'; +import '../job.dart'; + +final jobControllerProvider = + Provider.family.autoDispose( + (ref, userId) => JobController( + jobService: ref.watch(jobServiceProvider), + firebaseStorageService: ref.watch(firebaseStorageServiceProvider), + userId: userId, + ), +); + +class JobController { + const JobController({ + required JobService jobService, + required FirebaseStorageService firebaseStorageService, + required String userId, + }) : _jobService = jobService, + _firebaseStorageService = firebaseStorageService, + _userId = userId; + + /// storageの画像を保存するフォルダのパス + static const _storagePath = 'jobs'; + + final JobService _jobService; + final FirebaseStorageService _firebaseStorageService; + final String _userId; + + /// [Job] の情報を作成する。 + Future create({ + required String hostId, + required String title, + required String content, + required String place, + required Set accessTypes, + required String accessDescription, + required String belongings, + required String reward, + required String comment, + File? imageFile, + }) async { + var imageUrl = ''; + if (imageFile != null) { + imageUrl = await _uploadImage(imageFile); + } + await _jobService.create( + hostId: hostId, + title: title, + content: content, + place: place, + accessTypes: accessTypes, + accessDescription: accessDescription, + belongings: belongings, + reward: reward, + comment: comment, + imageUrl: imageUrl, + ); + } + + /// [Job] を更新する。 + Future updateJob({ + required String jobId, + String? title, + String? content, + String? place, + Set? accessTypes, + String? accessDescription, + String? belongings, + String? reward, + String? comment, + File? imageFile, + }) async { + String? imageUrl; + if (imageFile != null) { + imageUrl = await _uploadImage(imageFile); + } + await _jobService.update( + jobId: jobId, + title: title, + content: content, + place: place, + accessTypes: accessTypes, + accessDescription: accessDescription, + belongings: belongings, + reward: reward, + comment: comment, + imageUrl: imageUrl, + ); + } + + Future _uploadImage(File imageFile) { + final imagePath = '$_storagePath/$_userId-${DateTime.now()}.jpg'; + return _firebaseStorageService.upload( + path: imagePath, + resource: FirebaseStorageFile(imageFile), + ); + } +} diff --git a/packages/mottai_flutter_app/lib/job/ui/job_create.dart b/packages/mottai_flutter_app/lib/job/ui/job_create.dart new file mode 100644 index 00000000..08488a74 --- /dev/null +++ b/packages/mottai_flutter_app/lib/job/ui/job_create.dart @@ -0,0 +1,32 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +import '../../auth/ui/auth_dependent_builder.dart'; +import 'job_form.dart'; + +/// 仕事情報更新ページ。 +@RoutePage() +class JobCreatePage extends ConsumerWidget { + const JobCreatePage({ + super.key, + }); + + static const path = '/jobs/create'; + + /// [JobCreatePage] に遷移する際に `context.router.pushNamed` で指定する文字列。 + static const location = path; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Scaffold( + appBar: AppBar(title: const Text('お手伝い募集内容を入力')), + // TODO:なるんさんが実装中の「本人かどうかビルダー」に後で書き換える。 + body: AuthDependentBuilder( + onAuthenticated: (hostId) { + return JobForm.create(hostId: hostId); + }, + ), + ); + } +} diff --git a/packages/mottai_flutter_app/lib/job/ui/job_detail.dart b/packages/mottai_flutter_app/lib/job/ui/job_detail.dart index 4cc00190..2bed5e87 100644 --- a/packages/mottai_flutter_app/lib/job/ui/job_detail.dart +++ b/packages/mottai_flutter_app/lib/job/ui/job_detail.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '../../auth/auth.dart'; import '../../auth/ui/auth_dependent_builder.dart'; import '../../chat/chat_rooms.dart'; import '../../chat/ui/chat_room.dart'; @@ -12,6 +13,7 @@ import '../../scaffold_messenger_controller.dart'; import '../../user/host.dart'; import '../../user/user_mode.dart'; import '../job.dart'; +import 'job_update.dart'; import 'start_chat_with_host_controller.dart'; /// 仕事詳細ページ。 @@ -32,16 +34,28 @@ class JobDetailPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final userId = ref.watch(userIdProvider); + final jobAsyncValue = ref.watch(jobFutureProvider(jobId)); + final isLoading = jobAsyncValue.isLoading; + final job = ref.watch(jobFutureProvider(jobId)).valueOrNull; return Scaffold( - appBar: AppBar(title: const Text('お手伝い募集')), - body: ref.watch(jobFutureProvider(jobId)).when( - data: (job) { - if (job == null) { - return const Center( - child: Text('お手伝いの情報が見つかりませんでした。'), - ); - } - return ref.watch(hostFutureProvider(job.hostId)).when( + appBar: AppBar( + title: const Text('お手伝い募集'), + // TODO:なるんさんが実装中の「本人かどうかビルダー」に後で書き換える。 + actions: [ + if (!isLoading && userId != null && job != null) + IconButton( + onPressed: () => context.router + .pushNamed(JobUpdatePage.location(jobId: jobId)), + icon: const Icon(Icons.edit), + ), + ], + ), + body: isLoading + ? const Center(child: CircularProgressIndicator()) + : job == null + ? const Text('お手伝い募集の情報が見つかりませんでした。') + : ref.watch(hostFutureProvider(job.hostId)).when( data: (readHost) { if (readHost == null) { return const Center( @@ -52,16 +66,10 @@ class JobDetailPage extends ConsumerWidget { }, loading: () => const Center(child: CircularProgressIndicator()), - error: (error, stackTrace) => const Center( - child: Text('通信に失敗しました。'), + error: (_, __) => const Center( + child: Text('ホスト情報の取得に失敗しました。'), ), - ); - }, - loading: () => const Center(child: CircularProgressIndicator()), - error: (error, stackTrace) => const Center( - child: Text('お手伝いの情報の取得に失敗しました。'), - ), - ), + ), ); } } diff --git a/packages/mottai_flutter_app/lib/job/ui/job_form.dart b/packages/mottai_flutter_app/lib/job/ui/job_form.dart new file mode 100644 index 00000000..9963881a --- /dev/null +++ b/packages/mottai_flutter_app/lib/job/ui/job_form.dart @@ -0,0 +1,371 @@ +import 'package:dart_flutter_common/dart_flutter_common.dart'; +import 'package:firebase_common/firebase_common.dart'; +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +import '../../development/firebase_storage/firebase_storage.dart'; +import '../../development/firebase_storage/ui/firebase_storage_controller.dart'; +import 'job_controller.dart'; + +/// - `create` の場合、ログイン済みの `hostId`(ユーザー ID) +/// - `update` の場合、更新対象の [Job] とその本人であることが確認された `hostId`(ユーザー ID) +/// +/// を受け取り、それに応じた [Job] の作成または更新を行うフォーム。 +class JobForm extends ConsumerStatefulWidget { + const JobForm.create({ + required String hostId, + super.key, + }) : _hostId = hostId, + _job = null; + + const JobForm.update({ + required String hostId, + required ReadJob job, + super.key, + }) : _hostId = hostId, + _job = job; + + final ReadJob? _job; + + final String _hostId; + + @override + JobFormState createState() => JobFormState(); +} + +class JobFormState extends ConsumerState { + /// フォームのグローバルキー + final formKey = GlobalKey(); + + /// 選択中のアクセスタイプ + final List _selectedAccessTypes = []; + + /// [Job.title] のテキストフィールド用コントローラー + late final TextEditingController _titleController; + + /// [Job.place] のテキストフィールド用コントローラー + late final TextEditingController _locationController; + + /// [Job.content] のテキストフィールド用コントローラー + late final TextEditingController _contentController; + + /// [Job.belongings] のテキストフィールド用コントローラー + late final TextEditingController _belongingsController; + + /// [Job.reward] のテキストフィールド用コントローラー + late final TextEditingController _rewardController; + + /// [Job.accessDescription] のテキストフィールド用コントローラー + late final TextEditingController _accessDescriptionController; + + /// [Job.comment] のテキストフィールド用コントローラー + late final TextEditingController _commentController; + + /// 画像の高さ。 + static const double _imageHeight = 300; + + @override + void initState() { + super.initState(); + _titleController = TextEditingController(text: widget._job?.title); + _locationController = TextEditingController(text: widget._job?.place); + _contentController = TextEditingController(text: widget._job?.content); + _belongingsController = + TextEditingController(text: widget._job?.belongings); + _rewardController = TextEditingController(text: widget._job?.reward); + _accessDescriptionController = + TextEditingController(text: widget._job?.accessDescription); + _commentController = TextEditingController(text: widget._job?.comment); + if (widget._job != null) { + _selectedAccessTypes.addAll(widget._job!.accessTypes.toList()); + } + } + + @override + Widget build(BuildContext context) { + final firebaseStorageController = + ref.watch(firebaseStorageControllerProvider); + final pickedImageFile = ref.watch(pickedImageFileStateProvider); + final controller = ref.watch(jobControllerProvider(widget._hostId)); + final jobId = widget._job?.jobId; + return SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (pickedImageFile != null) + GestureDetector( + onTap: firebaseStorageController.pickImageFromGallery, + child: SizedBox( + height: _imageHeight, + child: Center( + child: Image.file(pickedImageFile), + ), + ), + ) + else if ((widget._job?.imageUrl ?? '').isNotEmpty) + GenericImage.rectangle( + onTap: firebaseStorageController.pickImageFromGallery, + showDetailOnTap: false, + imageUrl: pickedImageFile?.path ?? widget._job!.imageUrl, + height: _imageHeight, + width: null, + ) + else + GestureDetector( + onTap: firebaseStorageController.pickImageFromGallery, + child: Container( + height: _imageHeight, + decoration: BoxDecoration( + border: Border.all(color: Colors.black38), + ), + child: const Center(child: Icon(Icons.image)), + ), + ), + const Gap(32), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Form( + key: formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _TextInputSection( + title: 'お手伝いのタイトル', + description: 'お手伝いのタイトルを最大2行程度で入力してください。', + maxLines: 2, + defaultDisplayLines: 2, + controller: _titleController, + isRequired: true, + ), + _TextInputSection( + title: 'お手伝いの場所', + description: 'お手伝いを行う場所(農場や作業場所など)を入力してください。' + '作業内容や曜日によって複数の場所の可能性がある場合は、それも入力してください。', + controller: _locationController, + isRequired: true, + ), + _TextInputSection( + title: 'お手伝いの内容', + description: 'お手伝いの作業内容、作業時間帯やその他の情報をできるだけ詳しくを入力してください。' + 'お手伝い可能な曜日や時間帯、時期や季節が限られている場合や、' + 'その他に事前にお知らせするべき条件や情報などがあれば、その内容も入力してください。', + defaultDisplayLines: 10, + controller: _contentController, + isRequired: true, + ), + _TextInputSection( + title: '持ち物', + description: 'お手伝いに必要な服装や持ち物などを書いてください。' + '特に必要ない場合や貸出を行う場合はその内容も入力してください。', + controller: _belongingsController, + isRequired: true, + ), + _TextInputSection( + title: '報酬', + description: 'お手伝いをしてくれたワーカーにお渡しする報酬(食べ物など)を入力してください。', + controller: _rewardController, + isRequired: true, + ), + _TextInputSection.withChoice( + title: 'アクセス', + description: 'お手伝いの場所までのアクセス方法について補足説明をしてください。' + '最寄りの駅やバス停まで送迎ができる場合などは、その内容も入力してください。', + controller: _accessDescriptionController, + choices: { + for (final v in AccessType.values) v: v.label, + }, + enabledChoices: _selectedAccessTypes, + onChoiceSelected: (item) { + if (_selectedAccessTypes.contains(item)) { + _selectedAccessTypes.remove(item); + } else { + _selectedAccessTypes.add(item); + } + setState(() {}); + }, + ), + _TextInputSection( + title: 'ひとこと', + description: 'お手伝いを検討してくれるワーカーの方が、ぜひお手伝いをしてみたくなるようひとことや、' + '募集するお手伝いの魅力を入力しましょう!', + defaultDisplayLines: 5, + controller: _commentController, + ), + Padding( + padding: const EdgeInsets.only(top: 16, bottom: 32), + child: Center( + child: ElevatedButton( + onPressed: () { + final isValidate = formKey.currentState?.validate(); + if (!(isValidate ?? true)) { + return; + } + + if (jobId != null) { + controller.updateJob( + jobId: jobId, + title: _titleController.text, + place: _locationController.text, + content: _contentController.text, + belongings: _belongingsController.text, + reward: _rewardController.text, + accessDescription: + _accessDescriptionController.text, + comment: _commentController.text, + imageFile: pickedImageFile, + accessTypes: _selectedAccessTypes.toSet(), + ); + } else { + controller.create( + hostId: widget._hostId, + title: _titleController.text, + place: _locationController.text, + content: _contentController.text, + belongings: _belongingsController.text, + reward: _rewardController.text, + accessDescription: + _accessDescriptionController.text, + comment: _commentController.text, + imageFile: pickedImageFile, + accessTypes: _selectedAccessTypes.toSet(), + ); + } + }, + child: const Text('この内容で登録する'), + ), + ), + ), + ], + ), + ), + ), + ], + ), + ); + } +} + +/// タイトルと説明、テキストフィールドからなるセクション。 +/// [Section] を使用し、`Section.content` にフィールドを与えている。 +class _TextInputSection extends StatelessWidget { + /// テキスト入力のみをさせる通常の [_TextInputSection] を作成する。 + const _TextInputSection({ + required this.title, + this.description, + this.maxLines, + this.defaultDisplayLines = 1, + this.controller, + this.isRequired = false, + }) : choices = const {}, + enabledChoices = const [], + onChoiceSelected = null; + + /// テキスト入力と選択肢を併せて入力させる [_TextInputSection] を作成する。 + const _TextInputSection.withChoice({ + required this.title, + this.description, + this.maxLines, + this.defaultDisplayLines = 1, + required this.choices, + required this.enabledChoices, + required this.onChoiceSelected, + this.controller, + this.isRequired = false, + }); + + /// セクションのタイトル。 + final String title; + + /// セクションの説明。 + final String? description; + + /// テキストフィールドの最大行数 + final int? maxLines; + + /// 初期表示時のテキストフィールドの行数 + final int defaultDisplayLines; + + /// テキストフィールドの下に表示する選択肢 + /// 選択された際の値がkeyで、表示する値がvalueの[Map]で受け取る。 + final Map choices; + + /// [choices] の有効な値のリスト + final List enabledChoices; + + /// [choices] が選択された際のコールバック + final void Function(T item)? onChoiceSelected; + + /// テキストフィールドのコントローラー + final TextEditingController? controller; + + /// 必須入力か否か + final bool isRequired; + + @override + Widget build(BuildContext context) { + return Section( + title: title, + titleBadge: Padding( + padding: const EdgeInsets.only(left: 8), + child: _OptionalBadge(isRequired: isRequired), + ), + titleStyle: Theme.of(context).textTheme.titleLarge, + description: description, + descriptionStyle: Theme.of(context).textTheme.bodyMedium, + sectionPadding: const EdgeInsets.only(bottom: 32), + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextFormField( + controller: controller, + maxLines: maxLines, + validator: (value) { + if (isRequired && (value ?? '').isEmpty) { + return '入力してください。'; + } + return null; + }, + decoration: InputDecoration( + hintText: List.filled(defaultDisplayLines - 1, '\n').join(), + border: const OutlineInputBorder(), + ), + ), + if (choices.isNotEmpty) ...[ + const Gap(16), + SelectableChips( + allItems: choices.keys, + labels: choices, + runSpacing: 8, + enabledItems: enabledChoices, + onTap: onChoiceSelected, + ), + ], + ], + ), + ); + } +} + +/// 入力が必須か任意かを表すバッジ +/// [isRequired] の値によって、必須か任意の文字を選択して返す。 +class _OptionalBadge extends StatelessWidget { + const _OptionalBadge({this.isRequired = false}); + + /// 必須か否か + final bool isRequired; + + @override + Widget build(BuildContext context) { + if (isRequired) { + return const Badge( + label: Text('必須'), + ); + } else { + return const Badge( + label: Text('任意'), + backgroundColor: Colors.grey, + ); + } + } +} diff --git a/packages/mottai_flutter_app/lib/job/ui/job_update.dart b/packages/mottai_flutter_app/lib/job/ui/job_update.dart new file mode 100644 index 00000000..fbc7a6e3 --- /dev/null +++ b/packages/mottai_flutter_app/lib/job/ui/job_update.dart @@ -0,0 +1,54 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +import '../../auth/ui/auth_dependent_builder.dart'; +import '../job.dart'; +import 'job_form.dart'; + +/// 仕事情報更新ページ。 +@RoutePage() +class JobUpdatePage extends ConsumerWidget { + const JobUpdatePage({ + @PathParam('jobId') required this.jobId, + super.key, + }); + + static const path = '/jobs/:jobId/update'; + + /// [JobUpdatePage] に遷移する際に `context.router.pushNamed` で指定する文字列。 + static String location({required String jobId}) => '/jobs/$jobId/update'; + + final String jobId; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Scaffold( + appBar: AppBar(title: const Text('お手伝い募集内容を入力')), + // TODO:なるんさんが実装中の「本人かどうかビルダー」に後で書き換える。 + // すると 「編集の権限がありません。」のチェックはその時点で済んでいることになる。 + body: AuthDependentBuilder( + onAuthenticated: (hostId) { + return ref.watch(jobFutureProvider(jobId)).when( + data: (job) { + if (job == null) { + return const Center(child: Text('お手伝いが存在していません。')); + } + // UrlのhostIdとログイン中のユーザーのhostIdが違う場合 + if (job.hostId != hostId) { + return const Center( + child: Text('編集の権限がありません。'), + ); + } + return JobForm.update(hostId: hostId, job: job); + }, + loading: () => const Center(child: CircularProgressIndicator()), + error: (_, __) => const Center( + child: Text('仕事情報が取得できませんでした。'), + ), + ); + }, + ), + ); + } +} diff --git a/packages/mottai_flutter_app/lib/router/router.dart b/packages/mottai_flutter_app/lib/router/router.dart index 87e09a6b..26b6a864 100644 --- a/packages/mottai_flutter_app/lib/router/router.dart +++ b/packages/mottai_flutter_app/lib/router/router.dart @@ -17,7 +17,9 @@ import '../development/user_social_login/user_social_login.dart'; import '../development/web_link/ui/web_link_stub.dart'; import '../host/ui/create_or_update_host.dart'; import '../host/ui/host.dart'; +import '../job/ui/job_create.dart'; import '../job/ui/job_detail.dart'; +import '../job/ui/job_update.dart'; import '../map/ui/map.dart'; import '../my_account/ui/my_account.dart'; import '../review/ui/reviews.dart'; @@ -56,10 +58,18 @@ class AppRouter extends $AppRouter { path: ChatRoomPage.path, page: ChatRoomRoute.page, ), + AutoRoute( + path: JobCreatePage.path, + page: JobCreateRoute.page, + ), AutoRoute( path: JobDetailPage.path, page: JobDetailRoute.page, ), + AutoRoute( + path: JobUpdatePage.path, + page: JobUpdateRoute.page, + ), AutoRoute( path: WorkerPage.path, page: WorkerRoute.page, @@ -69,10 +79,6 @@ class AppRouter extends $AppRouter { page: HostRoute.page, ), // AutoRoute( - // path: CreateOrUpdateJobPage.path, - // page: CreateOrUpdateJobRoute.page, - // ), - // AutoRoute( // path: UserPage.path, // page: UserRoute.page, // ), diff --git a/packages/mottai_flutter_app/lib/router/router.gr.dart b/packages/mottai_flutter_app/lib/router/router.gr.dart index 0204a0ce..52032d55 100644 --- a/packages/mottai_flutter_app/lib/router/router.gr.dart +++ b/packages/mottai_flutter_app/lib/router/router.gr.dart @@ -8,8 +8,8 @@ // coverage:ignore-file // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:auto_route/auto_route.dart' as _i25; -import 'package:flutter/material.dart' as _i26; +import 'package:auto_route/auto_route.dart' as _i27; +import 'package:flutter/material.dart' as _i28; import 'package:mottai_flutter_app/chat/ui/chat_room.dart' as _i1; import 'package:mottai_flutter_app/chat/ui/chat_rooms.dart' as _i2; import 'package:mottai_flutter_app/development/color/ui/color.dart' as _i3; @@ -30,34 +30,36 @@ import 'package:mottai_flutter_app/development/image_picker/ui/image_picker_samp import 'package:mottai_flutter_app/development/in_review/ui/in_review.dart' as _i14; import 'package:mottai_flutter_app/development/sample_todo/ui/sample_todos.dart' - as _i20; -import 'package:mottai_flutter_app/development/sign_in/ui/sign_in.dart' as _i21; -import 'package:mottai_flutter_app/development/user_social_login/user_social_login.dart' as _i22; +import 'package:mottai_flutter_app/development/sign_in/ui/sign_in.dart' as _i23; +import 'package:mottai_flutter_app/development/user_social_login/user_social_login.dart' + as _i24; import 'package:mottai_flutter_app/development/web_link/ui/web_link_stub.dart' - as _i23; + as _i25; import 'package:mottai_flutter_app/host/ui/create_or_update_host.dart' as _i4; import 'package:mottai_flutter_app/host/ui/host.dart' as _i11; -import 'package:mottai_flutter_app/job/ui/job_detail.dart' as _i15; -import 'package:mottai_flutter_app/map/ui/map.dart' as _i16; -import 'package:mottai_flutter_app/my_account/ui/my_account.dart' as _i17; -import 'package:mottai_flutter_app/review/ui/reviews.dart' as _i18; -import 'package:mottai_flutter_app/root/ui/root.dart' as _i19; +import 'package:mottai_flutter_app/job/ui/job_create.dart' as _i15; +import 'package:mottai_flutter_app/job/ui/job_detail.dart' as _i16; +import 'package:mottai_flutter_app/job/ui/job_update.dart' as _i17; +import 'package:mottai_flutter_app/map/ui/map.dart' as _i18; +import 'package:mottai_flutter_app/my_account/ui/my_account.dart' as _i19; +import 'package:mottai_flutter_app/review/ui/reviews.dart' as _i20; +import 'package:mottai_flutter_app/root/ui/root.dart' as _i21; import 'package:mottai_flutter_app/worker/ui/create_or_update_worker.dart' as _i5; -import 'package:mottai_flutter_app/worker/ui/worker.dart' as _i24; +import 'package:mottai_flutter_app/worker/ui/worker.dart' as _i26; -abstract class $AppRouter extends _i25.RootStackRouter { +abstract class $AppRouter extends _i27.RootStackRouter { $AppRouter({super.navigatorKey}); @override - final Map pagesMap = { + final Map pagesMap = { ChatRoomRoute.name: (routeData) { final pathParams = routeData.inheritedPathParams; final args = routeData.argsAs( orElse: () => ChatRoomRouteArgs( chatRoomId: pathParams.getString('chatRoomId'))); - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, child: _i1.ChatRoomPage( chatRoomId: args.chatRoomId, @@ -66,13 +68,13 @@ abstract class $AppRouter extends _i25.RootStackRouter { ); }, ChatRoomsRoute.name: (routeData) { - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, child: const _i2.ChatRoomsPage(), ); }, ColorRoute.name: (routeData) { - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, child: const _i3.ColorPage(), ); @@ -84,7 +86,7 @@ abstract class $AppRouter extends _i25.RootStackRouter { userId: pathParams.getString('userId'), actionType: pathParams.getString('actionType'), )); - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, child: _i4.CreateOrUpdateHostPage( userId: args.userId, @@ -98,7 +100,7 @@ abstract class $AppRouter extends _i25.RootStackRouter { final args = routeData.argsAs( orElse: () => CreateOrUpdateWorkerRouteArgs( userId: pathParams.getString('userId'))); - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, child: _i5.CreateOrUpdateWorkerPage( userId: args.userId, @@ -107,31 +109,31 @@ abstract class $AppRouter extends _i25.RootStackRouter { ); }, DevelopmentItemsRoute.name: (routeData) { - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, child: const _i6.DevelopmentItemsPage(), ); }, EmailAndPasswordSignInRoute.name: (routeData) { - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, child: const _i7.EmailAndPasswordSignInPage(), ); }, FirebaseStorageSampleRoute.name: (routeData) { - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, child: const _i8.FirebaseStorageSamplePage(), ); }, ForceUpdateSampleRoute.name: (routeData) { - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, child: const _i9.ForceUpdateSamplePage(), ); }, GenericImagesRoute.name: (routeData) { - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, child: const _i10.GenericImagesPage(), ); @@ -140,7 +142,7 @@ abstract class $AppRouter extends _i25.RootStackRouter { final pathParams = routeData.inheritedPathParams; final args = routeData.argsAs( orElse: () => HostRouteArgs(userId: pathParams.getString('userId'))); - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, child: _i11.HostPage( userId: args.userId, @@ -149,82 +151,101 @@ abstract class $AppRouter extends _i25.RootStackRouter { ); }, ImageDetailViewStubRoute.name: (routeData) { - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, child: const _i12.ImageDetailViewStubPage(), ); }, ImagePickerSampleRoute.name: (routeData) { - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, child: const _i13.ImagePickerSamplePage(), ); }, InReviewRoute.name: (routeData) { - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, child: const _i14.InReviewPage(), ); }, + JobCreateRoute.name: (routeData) { + return _i27.AutoRoutePage( + routeData: routeData, + child: const _i15.JobCreatePage(), + ); + }, JobDetailRoute.name: (routeData) { final pathParams = routeData.inheritedPathParams; final args = routeData.argsAs( orElse: () => JobDetailRouteArgs(jobId: pathParams.getString('jobId'))); - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, - child: _i15.JobDetailPage( + child: _i16.JobDetailPage( + jobId: args.jobId, + key: args.key, + ), + ); + }, + JobUpdateRoute.name: (routeData) { + final pathParams = routeData.inheritedPathParams; + final args = routeData.argsAs( + orElse: () => + JobUpdateRouteArgs(jobId: pathParams.getString('jobId'))); + return _i27.AutoRoutePage( + routeData: routeData, + child: _i17.JobUpdatePage( jobId: args.jobId, key: args.key, ), ); }, MapRoute.name: (routeData) { - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, - child: const _i16.MapPage(), + child: const _i18.MapPage(), ); }, MyAccountRoute.name: (routeData) { - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, - child: const _i17.MyAccountPage(), + child: const _i19.MyAccountPage(), ); }, ReviewsRoute.name: (routeData) { - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, - child: const _i18.ReviewsPage(), + child: const _i20.ReviewsPage(), ); }, RootRoute.name: (routeData) { - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, - child: const _i19.RootPage(), + child: const _i21.RootPage(), ); }, SampleTodosRoute.name: (routeData) { - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, - child: const _i20.SampleTodosPage(), + child: const _i22.SampleTodosPage(), ); }, SignInSampleRoute.name: (routeData) { - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, - child: const _i21.SignInSamplePage(), + child: const _i23.SignInSamplePage(), ); }, UserSocialLoginSampleRoute.name: (routeData) { - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, - child: const _i22.UserSocialLoginSamplePage(), + child: const _i24.UserSocialLoginSamplePage(), ); }, WebLinkStubRoute.name: (routeData) { - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, - child: const _i23.WebLinkStubPage(), + child: const _i25.WebLinkStubPage(), ); }, WorkerRoute.name: (routeData) { @@ -232,9 +253,9 @@ abstract class $AppRouter extends _i25.RootStackRouter { final args = routeData.argsAs( orElse: () => WorkerRouteArgs(userId: pathParams.getString('userId'))); - return _i25.AutoRoutePage( + return _i27.AutoRoutePage( routeData: routeData, - child: _i24.WorkerPage( + child: _i26.WorkerPage( userId: args.userId, key: args.key, ), @@ -245,11 +266,11 @@ abstract class $AppRouter extends _i25.RootStackRouter { /// generated route for /// [_i1.ChatRoomPage] -class ChatRoomRoute extends _i25.PageRouteInfo { +class ChatRoomRoute extends _i27.PageRouteInfo { ChatRoomRoute({ required String chatRoomId, - _i26.Key? key, - List<_i25.PageRouteInfo>? children, + _i28.Key? key, + List<_i27.PageRouteInfo>? children, }) : super( ChatRoomRoute.name, args: ChatRoomRouteArgs( @@ -262,8 +283,8 @@ class ChatRoomRoute extends _i25.PageRouteInfo { static const String name = 'ChatRoomRoute'; - static const _i25.PageInfo page = - _i25.PageInfo(name); + static const _i27.PageInfo page = + _i27.PageInfo(name); } class ChatRoomRouteArgs { @@ -274,7 +295,7 @@ class ChatRoomRouteArgs { final String chatRoomId; - final _i26.Key? key; + final _i28.Key? key; @override String toString() { @@ -284,8 +305,8 @@ class ChatRoomRouteArgs { /// generated route for /// [_i2.ChatRoomsPage] -class ChatRoomsRoute extends _i25.PageRouteInfo { - const ChatRoomsRoute({List<_i25.PageRouteInfo>? children}) +class ChatRoomsRoute extends _i27.PageRouteInfo { + const ChatRoomsRoute({List<_i27.PageRouteInfo>? children}) : super( ChatRoomsRoute.name, initialChildren: children, @@ -293,13 +314,13 @@ class ChatRoomsRoute extends _i25.PageRouteInfo { static const String name = 'ChatRoomsRoute'; - static const _i25.PageInfo page = _i25.PageInfo(name); + static const _i27.PageInfo page = _i27.PageInfo(name); } /// generated route for /// [_i3.ColorPage] -class ColorRoute extends _i25.PageRouteInfo { - const ColorRoute({List<_i25.PageRouteInfo>? children}) +class ColorRoute extends _i27.PageRouteInfo { + const ColorRoute({List<_i27.PageRouteInfo>? children}) : super( ColorRoute.name, initialChildren: children, @@ -307,18 +328,18 @@ class ColorRoute extends _i25.PageRouteInfo { static const String name = 'ColorRoute'; - static const _i25.PageInfo page = _i25.PageInfo(name); + static const _i27.PageInfo page = _i27.PageInfo(name); } /// generated route for /// [_i4.CreateOrUpdateHostPage] class CreateOrUpdateHostRoute - extends _i25.PageRouteInfo { + extends _i27.PageRouteInfo { CreateOrUpdateHostRoute({ required String userId, required String actionType, - _i26.Key? key, - List<_i25.PageRouteInfo>? children, + _i28.Key? key, + List<_i27.PageRouteInfo>? children, }) : super( CreateOrUpdateHostRoute.name, args: CreateOrUpdateHostRouteArgs( @@ -335,8 +356,8 @@ class CreateOrUpdateHostRoute static const String name = 'CreateOrUpdateHostRoute'; - static const _i25.PageInfo page = - _i25.PageInfo(name); + static const _i27.PageInfo page = + _i27.PageInfo(name); } class CreateOrUpdateHostRouteArgs { @@ -350,7 +371,7 @@ class CreateOrUpdateHostRouteArgs { final String actionType; - final _i26.Key? key; + final _i28.Key? key; @override String toString() { @@ -361,11 +382,11 @@ class CreateOrUpdateHostRouteArgs { /// generated route for /// [_i5.CreateOrUpdateWorkerPage] class CreateOrUpdateWorkerRoute - extends _i25.PageRouteInfo { + extends _i27.PageRouteInfo { CreateOrUpdateWorkerRoute({ required String userId, - _i26.Key? key, - List<_i25.PageRouteInfo>? children, + _i28.Key? key, + List<_i27.PageRouteInfo>? children, }) : super( CreateOrUpdateWorkerRoute.name, args: CreateOrUpdateWorkerRouteArgs( @@ -378,8 +399,8 @@ class CreateOrUpdateWorkerRoute static const String name = 'CreateOrUpdateWorkerRoute'; - static const _i25.PageInfo page = - _i25.PageInfo(name); + static const _i27.PageInfo page = + _i27.PageInfo(name); } class CreateOrUpdateWorkerRouteArgs { @@ -390,7 +411,7 @@ class CreateOrUpdateWorkerRouteArgs { final String userId; - final _i26.Key? key; + final _i28.Key? key; @override String toString() { @@ -400,8 +421,8 @@ class CreateOrUpdateWorkerRouteArgs { /// generated route for /// [_i6.DevelopmentItemsPage] -class DevelopmentItemsRoute extends _i25.PageRouteInfo { - const DevelopmentItemsRoute({List<_i25.PageRouteInfo>? children}) +class DevelopmentItemsRoute extends _i27.PageRouteInfo { + const DevelopmentItemsRoute({List<_i27.PageRouteInfo>? children}) : super( DevelopmentItemsRoute.name, initialChildren: children, @@ -409,13 +430,13 @@ class DevelopmentItemsRoute extends _i25.PageRouteInfo { static const String name = 'DevelopmentItemsRoute'; - static const _i25.PageInfo page = _i25.PageInfo(name); + static const _i27.PageInfo page = _i27.PageInfo(name); } /// generated route for /// [_i7.EmailAndPasswordSignInPage] -class EmailAndPasswordSignInRoute extends _i25.PageRouteInfo { - const EmailAndPasswordSignInRoute({List<_i25.PageRouteInfo>? children}) +class EmailAndPasswordSignInRoute extends _i27.PageRouteInfo { + const EmailAndPasswordSignInRoute({List<_i27.PageRouteInfo>? children}) : super( EmailAndPasswordSignInRoute.name, initialChildren: children, @@ -423,13 +444,13 @@ class EmailAndPasswordSignInRoute extends _i25.PageRouteInfo { static const String name = 'EmailAndPasswordSignInRoute'; - static const _i25.PageInfo page = _i25.PageInfo(name); + static const _i27.PageInfo page = _i27.PageInfo(name); } /// generated route for /// [_i8.FirebaseStorageSamplePage] -class FirebaseStorageSampleRoute extends _i25.PageRouteInfo { - const FirebaseStorageSampleRoute({List<_i25.PageRouteInfo>? children}) +class FirebaseStorageSampleRoute extends _i27.PageRouteInfo { + const FirebaseStorageSampleRoute({List<_i27.PageRouteInfo>? children}) : super( FirebaseStorageSampleRoute.name, initialChildren: children, @@ -437,13 +458,13 @@ class FirebaseStorageSampleRoute extends _i25.PageRouteInfo { static const String name = 'FirebaseStorageSampleRoute'; - static const _i25.PageInfo page = _i25.PageInfo(name); + static const _i27.PageInfo page = _i27.PageInfo(name); } /// generated route for /// [_i9.ForceUpdateSamplePage] -class ForceUpdateSampleRoute extends _i25.PageRouteInfo { - const ForceUpdateSampleRoute({List<_i25.PageRouteInfo>? children}) +class ForceUpdateSampleRoute extends _i27.PageRouteInfo { + const ForceUpdateSampleRoute({List<_i27.PageRouteInfo>? children}) : super( ForceUpdateSampleRoute.name, initialChildren: children, @@ -451,13 +472,13 @@ class ForceUpdateSampleRoute extends _i25.PageRouteInfo { static const String name = 'ForceUpdateSampleRoute'; - static const _i25.PageInfo page = _i25.PageInfo(name); + static const _i27.PageInfo page = _i27.PageInfo(name); } /// generated route for /// [_i10.GenericImagesPage] -class GenericImagesRoute extends _i25.PageRouteInfo { - const GenericImagesRoute({List<_i25.PageRouteInfo>? children}) +class GenericImagesRoute extends _i27.PageRouteInfo { + const GenericImagesRoute({List<_i27.PageRouteInfo>? children}) : super( GenericImagesRoute.name, initialChildren: children, @@ -465,16 +486,16 @@ class GenericImagesRoute extends _i25.PageRouteInfo { static const String name = 'GenericImagesRoute'; - static const _i25.PageInfo page = _i25.PageInfo(name); + static const _i27.PageInfo page = _i27.PageInfo(name); } /// generated route for /// [_i11.HostPage] -class HostRoute extends _i25.PageRouteInfo { +class HostRoute extends _i27.PageRouteInfo { HostRoute({ required String userId, - _i26.Key? key, - List<_i25.PageRouteInfo>? children, + _i28.Key? key, + List<_i27.PageRouteInfo>? children, }) : super( HostRoute.name, args: HostRouteArgs( @@ -487,8 +508,8 @@ class HostRoute extends _i25.PageRouteInfo { static const String name = 'HostRoute'; - static const _i25.PageInfo page = - _i25.PageInfo(name); + static const _i27.PageInfo page = + _i27.PageInfo(name); } class HostRouteArgs { @@ -499,7 +520,7 @@ class HostRouteArgs { final String userId; - final _i26.Key? key; + final _i28.Key? key; @override String toString() { @@ -509,8 +530,8 @@ class HostRouteArgs { /// generated route for /// [_i12.ImageDetailViewStubPage] -class ImageDetailViewStubRoute extends _i25.PageRouteInfo { - const ImageDetailViewStubRoute({List<_i25.PageRouteInfo>? children}) +class ImageDetailViewStubRoute extends _i27.PageRouteInfo { + const ImageDetailViewStubRoute({List<_i27.PageRouteInfo>? children}) : super( ImageDetailViewStubRoute.name, initialChildren: children, @@ -518,13 +539,13 @@ class ImageDetailViewStubRoute extends _i25.PageRouteInfo { static const String name = 'ImageDetailViewStubRoute'; - static const _i25.PageInfo page = _i25.PageInfo(name); + static const _i27.PageInfo page = _i27.PageInfo(name); } /// generated route for /// [_i13.ImagePickerSamplePage] -class ImagePickerSampleRoute extends _i25.PageRouteInfo { - const ImagePickerSampleRoute({List<_i25.PageRouteInfo>? children}) +class ImagePickerSampleRoute extends _i27.PageRouteInfo { + const ImagePickerSampleRoute({List<_i27.PageRouteInfo>? children}) : super( ImagePickerSampleRoute.name, initialChildren: children, @@ -532,13 +553,13 @@ class ImagePickerSampleRoute extends _i25.PageRouteInfo { static const String name = 'ImagePickerSampleRoute'; - static const _i25.PageInfo page = _i25.PageInfo(name); + static const _i27.PageInfo page = _i27.PageInfo(name); } /// generated route for /// [_i14.InReviewPage] -class InReviewRoute extends _i25.PageRouteInfo { - const InReviewRoute({List<_i25.PageRouteInfo>? children}) +class InReviewRoute extends _i27.PageRouteInfo { + const InReviewRoute({List<_i27.PageRouteInfo>? children}) : super( InReviewRoute.name, initialChildren: children, @@ -546,16 +567,30 @@ class InReviewRoute extends _i25.PageRouteInfo { static const String name = 'InReviewRoute'; - static const _i25.PageInfo page = _i25.PageInfo(name); + static const _i27.PageInfo page = _i27.PageInfo(name); } /// generated route for -/// [_i15.JobDetailPage] -class JobDetailRoute extends _i25.PageRouteInfo { +/// [_i15.JobCreatePage] +class JobCreateRoute extends _i27.PageRouteInfo { + const JobCreateRoute({List<_i27.PageRouteInfo>? children}) + : super( + JobCreateRoute.name, + initialChildren: children, + ); + + static const String name = 'JobCreateRoute'; + + static const _i27.PageInfo page = _i27.PageInfo(name); +} + +/// generated route for +/// [_i16.JobDetailPage] +class JobDetailRoute extends _i27.PageRouteInfo { JobDetailRoute({ required String jobId, - _i26.Key? key, - List<_i25.PageRouteInfo>? children, + _i28.Key? key, + List<_i27.PageRouteInfo>? children, }) : super( JobDetailRoute.name, args: JobDetailRouteArgs( @@ -568,8 +603,8 @@ class JobDetailRoute extends _i25.PageRouteInfo { static const String name = 'JobDetailRoute'; - static const _i25.PageInfo page = - _i25.PageInfo(name); + static const _i27.PageInfo page = + _i27.PageInfo(name); } class JobDetailRouteArgs { @@ -580,7 +615,7 @@ class JobDetailRouteArgs { final String jobId; - final _i26.Key? key; + final _i28.Key? key; @override String toString() { @@ -589,9 +624,48 @@ class JobDetailRouteArgs { } /// generated route for -/// [_i16.MapPage] -class MapRoute extends _i25.PageRouteInfo { - const MapRoute({List<_i25.PageRouteInfo>? children}) +/// [_i17.JobUpdatePage] +class JobUpdateRoute extends _i27.PageRouteInfo { + JobUpdateRoute({ + required String jobId, + _i28.Key? key, + List<_i27.PageRouteInfo>? children, + }) : super( + JobUpdateRoute.name, + args: JobUpdateRouteArgs( + jobId: jobId, + key: key, + ), + rawPathParams: {'jobId': jobId}, + initialChildren: children, + ); + + static const String name = 'JobUpdateRoute'; + + static const _i27.PageInfo page = + _i27.PageInfo(name); +} + +class JobUpdateRouteArgs { + const JobUpdateRouteArgs({ + required this.jobId, + this.key, + }); + + final String jobId; + + final _i28.Key? key; + + @override + String toString() { + return 'JobUpdateRouteArgs{jobId: $jobId, key: $key}'; + } +} + +/// generated route for +/// [_i18.MapPage] +class MapRoute extends _i27.PageRouteInfo { + const MapRoute({List<_i27.PageRouteInfo>? children}) : super( MapRoute.name, initialChildren: children, @@ -599,13 +673,13 @@ class MapRoute extends _i25.PageRouteInfo { static const String name = 'MapRoute'; - static const _i25.PageInfo page = _i25.PageInfo(name); + static const _i27.PageInfo page = _i27.PageInfo(name); } /// generated route for -/// [_i17.MyAccountPage] -class MyAccountRoute extends _i25.PageRouteInfo { - const MyAccountRoute({List<_i25.PageRouteInfo>? children}) +/// [_i19.MyAccountPage] +class MyAccountRoute extends _i27.PageRouteInfo { + const MyAccountRoute({List<_i27.PageRouteInfo>? children}) : super( MyAccountRoute.name, initialChildren: children, @@ -613,13 +687,13 @@ class MyAccountRoute extends _i25.PageRouteInfo { static const String name = 'MyAccountRoute'; - static const _i25.PageInfo page = _i25.PageInfo(name); + static const _i27.PageInfo page = _i27.PageInfo(name); } /// generated route for -/// [_i18.ReviewsPage] -class ReviewsRoute extends _i25.PageRouteInfo { - const ReviewsRoute({List<_i25.PageRouteInfo>? children}) +/// [_i20.ReviewsPage] +class ReviewsRoute extends _i27.PageRouteInfo { + const ReviewsRoute({List<_i27.PageRouteInfo>? children}) : super( ReviewsRoute.name, initialChildren: children, @@ -627,13 +701,13 @@ class ReviewsRoute extends _i25.PageRouteInfo { static const String name = 'ReviewsRoute'; - static const _i25.PageInfo page = _i25.PageInfo(name); + static const _i27.PageInfo page = _i27.PageInfo(name); } /// generated route for -/// [_i19.RootPage] -class RootRoute extends _i25.PageRouteInfo { - const RootRoute({List<_i25.PageRouteInfo>? children}) +/// [_i21.RootPage] +class RootRoute extends _i27.PageRouteInfo { + const RootRoute({List<_i27.PageRouteInfo>? children}) : super( RootRoute.name, initialChildren: children, @@ -641,13 +715,13 @@ class RootRoute extends _i25.PageRouteInfo { static const String name = 'RootRoute'; - static const _i25.PageInfo page = _i25.PageInfo(name); + static const _i27.PageInfo page = _i27.PageInfo(name); } /// generated route for -/// [_i20.SampleTodosPage] -class SampleTodosRoute extends _i25.PageRouteInfo { - const SampleTodosRoute({List<_i25.PageRouteInfo>? children}) +/// [_i22.SampleTodosPage] +class SampleTodosRoute extends _i27.PageRouteInfo { + const SampleTodosRoute({List<_i27.PageRouteInfo>? children}) : super( SampleTodosRoute.name, initialChildren: children, @@ -655,13 +729,13 @@ class SampleTodosRoute extends _i25.PageRouteInfo { static const String name = 'SampleTodosRoute'; - static const _i25.PageInfo page = _i25.PageInfo(name); + static const _i27.PageInfo page = _i27.PageInfo(name); } /// generated route for -/// [_i21.SignInSamplePage] -class SignInSampleRoute extends _i25.PageRouteInfo { - const SignInSampleRoute({List<_i25.PageRouteInfo>? children}) +/// [_i23.SignInSamplePage] +class SignInSampleRoute extends _i27.PageRouteInfo { + const SignInSampleRoute({List<_i27.PageRouteInfo>? children}) : super( SignInSampleRoute.name, initialChildren: children, @@ -669,13 +743,13 @@ class SignInSampleRoute extends _i25.PageRouteInfo { static const String name = 'SignInSampleRoute'; - static const _i25.PageInfo page = _i25.PageInfo(name); + static const _i27.PageInfo page = _i27.PageInfo(name); } /// generated route for -/// [_i22.UserSocialLoginSamplePage] -class UserSocialLoginSampleRoute extends _i25.PageRouteInfo { - const UserSocialLoginSampleRoute({List<_i25.PageRouteInfo>? children}) +/// [_i24.UserSocialLoginSamplePage] +class UserSocialLoginSampleRoute extends _i27.PageRouteInfo { + const UserSocialLoginSampleRoute({List<_i27.PageRouteInfo>? children}) : super( UserSocialLoginSampleRoute.name, initialChildren: children, @@ -683,13 +757,13 @@ class UserSocialLoginSampleRoute extends _i25.PageRouteInfo { static const String name = 'UserSocialLoginSampleRoute'; - static const _i25.PageInfo page = _i25.PageInfo(name); + static const _i27.PageInfo page = _i27.PageInfo(name); } /// generated route for -/// [_i23.WebLinkStubPage] -class WebLinkStubRoute extends _i25.PageRouteInfo { - const WebLinkStubRoute({List<_i25.PageRouteInfo>? children}) +/// [_i25.WebLinkStubPage] +class WebLinkStubRoute extends _i27.PageRouteInfo { + const WebLinkStubRoute({List<_i27.PageRouteInfo>? children}) : super( WebLinkStubRoute.name, initialChildren: children, @@ -697,16 +771,16 @@ class WebLinkStubRoute extends _i25.PageRouteInfo { static const String name = 'WebLinkStubRoute'; - static const _i25.PageInfo page = _i25.PageInfo(name); + static const _i27.PageInfo page = _i27.PageInfo(name); } /// generated route for -/// [_i24.WorkerPage] -class WorkerRoute extends _i25.PageRouteInfo { +/// [_i26.WorkerPage] +class WorkerRoute extends _i27.PageRouteInfo { WorkerRoute({ required String userId, - _i26.Key? key, - List<_i25.PageRouteInfo>? children, + _i28.Key? key, + List<_i27.PageRouteInfo>? children, }) : super( WorkerRoute.name, args: WorkerRouteArgs( @@ -719,8 +793,8 @@ class WorkerRoute extends _i25.PageRouteInfo { static const String name = 'WorkerRoute'; - static const _i25.PageInfo page = - _i25.PageInfo(name); + static const _i27.PageInfo page = + _i27.PageInfo(name); } class WorkerRouteArgs { @@ -731,7 +805,7 @@ class WorkerRouteArgs { final String userId; - final _i26.Key? key; + final _i28.Key? key; @override String toString() {