sojorn/sojorn_app/lib/widgets/reactions/reaction_picker.dart
Patrick Britton 94ffb419ae Replace appreciate button with smart reaction button
- Remove appreciate button functionality (redundant with reactions)
- Create SmartReactionButton that shows:
  - Plus icon when no reactions exist
  - Top reaction + count when reactions exist
  - User's reaction + count when user has reacted
- Update ReactionPicker to show existing reactions first with counts
- Add visual indicators for selected reactions and counts
- Maintain full reaction functionality in single button
- Improve UX by consolidating reaction interactions
2026-02-01 14:16:20 -06:00

167 lines
5.7 KiB
Dart

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import '../../theme/app_theme.dart';
class ReactionPicker extends StatefulWidget {
final Function(String) onReactionSelected;
final VoidCallback? onClosed;
final List<String>? reactions;
final Map<String, int>? reactionCounts;
final Set<String>? myReactions;
const ReactionPicker({
super.key,
required this.onReactionSelected,
this.onClosed,
this.reactions,
this.reactionCounts,
this.myReactions,
});
@override
State<ReactionPicker> createState() => _ReactionPickerState();
}
class _ReactionPickerState extends State<ReactionPicker> {
static const List<String> _commonReactions = [
'❤️', '👍', '😂', '😮', '😢', '😡',
'🎉', '🔥', '👏', '🙏', '💯', '🤔',
'😍', '🤣', '😊', '👌', '🙌', '💪',
'🎯', '', '', '🌟', '💫', '☀️',
];
@override
Widget build(BuildContext context) {
final reactions = widget.reactions ?? _commonReactions;
final reactionCounts = widget.reactionCounts ?? {};
final myReactions = widget.myReactions ?? {};
return Dialog(
backgroundColor: Colors.transparent,
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: AppTheme.cardSurface,
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: AppTheme.navyBlue.withValues(alpha: 0.2),
width: 1,
),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.2),
blurRadius: 20,
offset: const Offset(0, 10),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Header
Row(
children: [
Text(
'Add Reaction',
style: GoogleFonts.inter(
color: AppTheme.navyBlue,
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
const Spacer(),
IconButton(
onPressed: () {
Navigator.of(context).pop();
widget.onClosed?.call();
},
icon: Icon(
Icons.close,
color: AppTheme.textSecondary,
size: 20,
),
),
],
),
const SizedBox(height: 16),
// Emoji grid
GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 6,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
childAspectRatio: 1,
),
itemCount: reactions.length,
itemBuilder: (context, index) {
final emoji = reactions[index];
final count = reactionCounts[emoji] ?? 0;
final isSelected = myReactions.contains(emoji);
return Material(
color: Colors.transparent,
child: InkWell(
onTap: () {
Navigator.of(context).pop();
widget.onReactionSelected(emoji);
},
borderRadius: BorderRadius.circular(12),
child: Container(
decoration: BoxDecoration(
color: isSelected
? AppTheme.brightNavy.withValues(alpha: 0.2)
: AppTheme.navyBlue.withValues(alpha: 0.05),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: isSelected
? AppTheme.brightNavy
: AppTheme.navyBlue.withValues(alpha: 0.1),
width: isSelected ? 2 : 1,
),
),
child: Stack(
children: [
Center(
child: Text(
emoji,
style: const TextStyle(fontSize: 24),
),
),
if (count > 0)
Positioned(
right: 2,
bottom: 2,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1),
decoration: BoxDecoration(
color: AppTheme.brightNavy,
borderRadius: BorderRadius.circular(8),
),
child: Text(
count > 99 ? '99+' : '$count',
style: GoogleFonts.inter(
color: Colors.white,
fontSize: 8,
fontWeight: FontWeight.w600,
),
),
),
),
],
),
),
),
);
},
),
const SizedBox(height: 16),
],
),
),
);
}
}