progress towards matching the design of save routine dialog.

This commit is contained in:
Faris Armoush
2025-04-17 16:14:26 +03:00
parent 2e4f904d3a
commit 84264391d9
3 changed files with 105 additions and 61 deletions

View File

@ -6,7 +6,6 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routines/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
@ -18,6 +17,10 @@ class SaveRoutineHelper {
builder: (BuildContext context) { builder: (BuildContext context) {
return BlocBuilder<RoutineBloc, RoutineState>( return BlocBuilder<RoutineBloc, RoutineState>(
builder: (context, state) { builder: (context, state) {
final selectedConditionLabel = state.selectedAutomationOperator == 'and'
? 'All Conditions are met'
: 'Any Condition is met';
return AlertDialog( return AlertDialog(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
content: Container( content: Container(
@ -29,22 +32,35 @@ class SaveRoutineHelper {
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
DialogHeader('Create a scene: ${state.routineName ?? ""}'), const SizedBox(height: 18),
Text(
'Create a scene: ${state.routineName ?? ""}',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.headlineMedium!.copyWith(
color: ColorsManager.primaryColorWithOpacity,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 18),
_buildDivider(),
_buildListsLabelRow(selectedConditionLabel),
Padding( Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsetsDirectional.symmetric(
horizontal: 16, horizontal: 16,
vertical: 8,
), ),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [ children: [
_buildIfConditions(state, context), _buildIfConditions(state, context),
const SizedBox(width: 16),
_buildThenActions(state, context), _buildThenActions(state, context),
], ],
), ),
), ),
_buildDivider(),
const SizedBox(height: 8),
_buildDialogFooter(context, state), _buildDialogFooter(context, state),
const SizedBox(height: 8),
], ],
), ),
), ),
@ -55,63 +71,82 @@ class SaveRoutineHelper {
); );
} }
static DialogFooter _buildDialogFooter(BuildContext context, RoutineState state) { static Container _buildDivider() {
return DialogFooter( return Container(
onCancel: () => Navigator.pop(context), height: 1,
onConfirm: () { width: double.infinity,
if (state.isAutomation) { color: ColorsManager.greyColor,
if (state.isUpdate ?? false) {
context.read<RoutineBloc>().add(const UpdateAutomation());
} else {
context.read<RoutineBloc>().add(const CreateAutomationEvent());
}
} else {
if (state.isUpdate ?? false) {
context.read<RoutineBloc>().add(const UpdateScene());
} else {
context.read<RoutineBloc>().add(const CreateSceneEvent());
}
}
Navigator.pop(context);
},
isConfirmEnabled: true,
); );
} }
static Expanded _buildThenActions(RoutineState state, BuildContext context) { static Widget _buildListsLabelRow(String selectedConditionLabel) {
return Expanded( const textStyle = TextStyle(
child: ListView( fontSize: 16,
shrinkWrap: true, );
return Container(
color: ColorsManager.backgroundColor.withValues(alpha: 0.5),
padding: const EdgeInsetsDirectional.all(20),
child: Row(
spacing: 16,
children: [ children: [
const Text( Expanded(child: Text('IF: $selectedConditionLabel', style: textStyle)),
'THEN:', const Expanded(child: Text('THEN:', style: textStyle)),
style: TextStyle(
fontSize: 16,
),
),
const SizedBox(height: 8),
...state.thenItems.map((item) {
final functions = state.selectedFunctions[item['uniqueCustomId']] ?? [];
return functionRow(item, context, functions);
}),
], ],
), ),
); );
} }
static Expanded _buildIfConditions(RoutineState state, BuildContext context) { static Widget _buildDialogFooter(BuildContext context, RoutineState state) {
return Row(
spacing: 16,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
DialogFooterButton(
text: 'Cancel',
onTap: () => Navigator.pop(context),
),
DialogFooterButton(
text: 'Confirm',
onTap: () {
if (state.isAutomation) {
if (state.isUpdate ?? false) {
context.read<RoutineBloc>().add(const UpdateAutomation());
} else {
context.read<RoutineBloc>().add(const CreateAutomationEvent());
}
} else {
if (state.isUpdate ?? false) {
context.read<RoutineBloc>().add(const UpdateScene());
} else {
context.read<RoutineBloc>().add(const CreateSceneEvent());
}
}
Navigator.pop(context);
},
textColor: ColorsManager.primaryColorWithOpacity,
),
],
);
}
static Widget _buildThenActions(RoutineState state, BuildContext context) {
return Expanded(
child: ListView(
shrinkWrap: true,
children: state.thenItems.map((item) {
final functions = state.selectedFunctions[item['uniqueCustomId']] ?? [];
return functionRow(item, context, functions);
}).toList(),
),
);
}
static Widget _buildIfConditions(RoutineState state, BuildContext context) {
return Expanded( return Expanded(
child: ListView( child: ListView(
shrinkWrap: true, shrinkWrap: true,
children: [ children: [
const Text(
'IF:',
style: TextStyle(
fontSize: 16,
),
),
const SizedBox(height: 8),
if (state.isTabToRun) if (state.isTabToRun)
ListTile( ListTile(
leading: SvgPicture.asset( leading: SvgPicture.asset(

View File

@ -28,32 +28,40 @@ class DialogFooter extends StatelessWidget {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
_buildFooterButton( DialogFooterButton(
context: context,
text: 'Cancel', text: 'Cancel',
onTap: onCancel, onTap: onCancel,
), ),
if (isConfirmEnabled) ...[ if (isConfirmEnabled) ...[
Container(width: 1, height: 50, color: ColorsManager.greyColor), Container(width: 1, height: 50, color: ColorsManager.greyColor),
_buildFooterButton( DialogFooterButton(
context: context,
text: 'Confirm', text: 'Confirm',
onTap: onConfirm, onTap: onConfirm,
textColor: textColor: isConfirmEnabled
isConfirmEnabled ? ColorsManager.primaryColorWithOpacity : Colors.red, ? ColorsManager.primaryColorWithOpacity
: Colors.red,
), ),
], ],
], ],
), ),
); );
} }
}
Widget _buildFooterButton({ class DialogFooterButton extends StatelessWidget {
required BuildContext context, const DialogFooterButton({
required String text, required this.text,
required VoidCallback? onTap, required this.onTap,
Color? textColor, this.textColor,
}) { super.key,
});
final String text;
final VoidCallback? onTap;
final Color? textColor;
@override
Widget build(BuildContext context) {
return Expanded( return Expanded(
child: TextButton( child: TextButton(
style: TextButton.styleFrom( style: TextButton.styleFrom(

View File

@ -16,6 +16,7 @@ class DialogHeader extends StatelessWidget {
), ),
Text( Text(
title, title,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyMedium!.copyWith( style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.primaryColorWithOpacity, color: ColorsManager.primaryColorWithOpacity,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,