MealVeda Day 3: Breakthrough - Implementing Advanced Bottom Sheet Navigation

Day 3 delivered MealVeda's most technically complex feature: the bottom sheet meal selection interface. This implementation required careful state management, smooth animations, and real-time data synchronization.
Bottom Sheet Architecture
The meal selection system uses Flutter's showModalBottomSheet with custom enhancements for contextual meal display:
void _showMealSelection(MealCategory category) {
showModalBottomSheet<Meal>(
context: context,
isScrollControlled: true,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
),
builder: (context) => MealSelectionSheet(
category: category,
availableMeals: _getMealsForCategory(category),
onMealSelected: _addMealToDay,
),
);
}
Dynamic Meal Filtering
Implemented contextual meal filtering based on category and time of day:
List<Meal> _getMealsForCategory(MealCategory category) {
return userMealCollection
.where((meal) =>
meal.category == category ||
meal.isAppropriateForTime(DateTime.now())
)
.take(8) // Limit to prevent overwhelming users
.toList();
}
extension MealTiming on Meal {
bool isAppropriateForTime(DateTime time) {
final hour = time.hour;
switch (category) {
case MealCategory.breakfast:
return hour >= 6 && hour < 11;
case MealCategory.lunch:
return hour >= 11 && hour < 16;
case MealCategory.dinner:
return hour >= 16 && hour < 22;
case MealCategory.snacks:
return true; // Snacks appropriate anytime
}
}
}
Real-Time State Synchronization
The critical technical challenge was maintaining state synchronization between the bottom sheet and parent screen:
class MealSelectionSheet extends StatefulWidget {
final MealCategory category;
final List<Meal> availableMeals;
final Function(Meal) onMealSelected;
@override
_MealSelectionSheetState createState() => _MealSelectionSheetState();
}
class _MealSelectionSheetState extends State<MealSelectionSheet> {
void _selectMeal(Meal meal) {
// Update parent state immediately
widget.onMealSelected(meal);
// Trigger nutrition recalculation
Provider.of<NutritionTracker>(context, listen: false)
.addMealToDay(meal);
// Close bottom sheet with animation
Navigator.of(context).pop(meal);
}
}
Inline Meal Creation
The "Create New Meal" functionality needed to work seamlessly within the bottom sheet context:
Widget _buildCreateNewOption() {
return ListTile(
leading: CircleAvatar(
backgroundColor: Colors.green,
child: Icon(Icons.add, color: Colors.white),
),
title: Text('Create New ${widget.category.name}'),
subtitle: Text('Add a new meal to your collection'),
onTap: () => _showInlineCreation(),
);
}
void _showInlineCreation() {
// Expand bottom sheet to full screen
Navigator.of(context).push(
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) =>
CreateMealScreen(
initialCategory: widget.category,
onMealCreated: (meal) {
widget.onMealSelected(meal);
Navigator.of(context).pop(); // Return to day view
},
),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return SlideTransition(
position: animation.drive(
Tween(begin: Offset(0.0, 1.0), end: Offset.zero)
),
child: child,
);
},
),
);
}
Performance Optimizations
Critical optimizations for smooth 60fps animations:
class OptimizedMealList extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView.builder(
// Use builder for memory efficiency
itemCount: widget.meals.length,
cacheExtent: 100, // Reduce render overhead
physics: BouncingScrollPhysics(),
itemBuilder: (context, index) {
return MealListTile(
meal: widget.meals[index],
onTap: () => _selectMeal(widget.meals[index]),
);
},
);
}
}
// Prevent unnecessary rebuilds
class MealListTile extends StatelessWidget {
final Meal meal;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return Card(
child: ListTile(
title: Text(meal.name),
subtitle: Text('${meal.calories} cal • ${meal.protein}g protein'),
trailing: Icon(Icons.add_circle_outline, color: Colors.green),
onTap: onTap,
),
);
}
}
Animation System
Implemented smooth slide animations for state transitions:
class SlideUpRoute<T> extends PageRouteBuilder<T> {
final Widget child;
SlideUpRoute({required this.child})
: super(
pageBuilder: (context, animation, secondaryAnimation) => child,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
const begin = Offset(0.0, 1.0);
const end = Offset.zero;
final tween = Tween(begin: begin, end: end);
final offsetAnimation = animation.drive(tween);
return SlideTransition(position: offsetAnimation, child: child);
},
);
}
Day 3 Technical Achievements:
Context-aware meal filtering and display
Real-time state synchronization across components
Inline meal creation with smooth transitions
60fps bottom sheet animations and interactions
Memory-efficient list rendering for large meal collections
This bottom sheet implementation became MealVeda's signature interaction pattern, differentiating it from traditional meal planning interfaces.



