Flutter – Create Fortune Wheel Spin
Deciding what to eat can be a daily dilemma, especially when you have a plethora of options to choose from. To make this decision-making process more exciting and interactive, in this article, we will be creating – the Lunch Spinning Wheel app. A sample video is given below to get an idea about what we are going to do in this article.
Prerequisites
Before we dive into the implementation, make sure Flutter is installed on the system or download it from the official Flutter website. Also, ensure that a code editor is installed, such as Visual Studio Code or Android Studio.
Step-by-Step Implementation
Step 1: Create Project
In the Visual Studio Code terminal run the below command to create a Flutter project:
Dart
flutter create lunch_spinning_wheel |
Step 2: Add libraries
Now we need to add the package into the pubspec.yaml file. From the command line:
Dart
flutter pub add flutter_fortune_wheel http confetti |
This will add all packages under dependencies section in pubspec.yaml file as shown below:
Step 3: Import libraries
Now, import these packages in main.dart file,
Dart
import 'package:http/http.dart' as http; import 'package:flutter_fortune_wheel/flutter_fortune_wheel.dart' ; import 'package:confetti/confetti.dart' ; |
Step 4: Create Lunch Class
Create Lunch class that requires the meal parameter to be provided when creating an instance of the Lunch class and the img parameter is optional. The fromJson factory method is used to create Lunch instances by extracting values from a JSON map.
Dart
class Lunch { final String meal; var img; Lunch({required this .meal, this .img}); factory Lunch.fromJson(Map<String, dynamic> json) { return Lunch(meal: json[ 'strMeal' ], img: json[ 'strMealThumb' ]); } } |
Step 5: Loading Lunch Ideas from an API
Fetch a list of lunch ideas from the MealDB API to retrieve a variety of Indian meal options.
Dart
String url = "https://www.themealdb.com/api/json/v1/1/filter.php?a=Indian" ; List<Lunch> _ideas = []; Future< void > _getLunchIdeas() async { http.Response response; Uri uri = Uri.parse(url); response = await http.get(uri); if (response.statusCode == 200) { Map<String, dynamic> jsonData = json.decode(response.body); if (jsonData[ 'meals' ] != null) { List<dynamic> meals = jsonData[ 'meals' ]; setState(() { _ideas = meals.map((json) => Lunch.fromJson(json)).toList(); }); } } } |
Step 6: Create the Spinning Wheel
The spinning wheel is implemented using the FortuneWheel widget provided by the Flutter Fortune Wheel package. It takes the list of lunch ideas and randomly selects one when the wheel is spun. The selected idea is displayed in alert dialog when wheel is stopped.
Dart
FortuneWheel( selected: selected.stream, items: [ for (var it in _ideas) FortuneItem(child: Text(it.meal)), ], onAnimationEnd: () { _centerController.play(); showDialog( barrierDismissible: true , context: context, builder: (BuildContext context) { return Center( child: AlertDialog( scrollable: true , title: Text( "Hurray! today's meal is????" ), content: Column( children: [ ConfettiWidget( confettiController: _centerController, blastDirection: pi / 2, maxBlastForce: 5, minBlastForce: 1, emissionFrequency: 0.03, numberOfParticles: 10, gravity: 0), SizedBox(height: 10), Text( selectedIdea, style: TextStyle(fontSize: 22), ), SizedBox(height: 20), Image.network(selectedImg), ], ), ), ); }); }, onFocusItemChanged: (value) { if (flag == true ) { setValue(value); } else { flag = true ; } }, ), |
Complete Code
Dart
import 'dart:async' ; import 'dart:convert' ; import 'dart:math' ; import 'package:http/http.dart' as http; import 'package:flutter/material.dart' ; import 'package:flutter_fortune_wheel/flutter_fortune_wheel.dart' ; import 'package:confetti/confetti.dart' ; void main() => runApp( const MyApp()); class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false , title: 'Gfg Lunch Wheel' , theme: ThemeData( primarySwatch: Colors.green, ), home: ExamplePage(), ); } } class ExamplePage extends StatefulWidget { @override _ExamplePageState createState() => _ExamplePageState(); } class Lunch { final String meal; var img; Lunch({required this .meal, this .img}); factory Lunch.fromJson(Map<String, dynamic> json) { return Lunch(meal: json[ 'strMeal' ], img: json[ 'strMealThumb' ]); } } class _ExamplePageState extends State<ExamplePage> { StreamController< int > selected = StreamController< int >(); late ConfettiController _centerController; String url = "https://www.themealdb.com/api/json/v1/1/filter.php?a=Indian" ; List<Lunch> _ideas = []; Future< void > _getLunchIdeas() async { http.Response response; Uri uri = Uri.parse(url); response = await http.get(uri); if (response.statusCode == 200) { Map<String, dynamic> jsonData = json.decode(response.body); if (jsonData[ 'meals' ] != null) { List<dynamic> meals = jsonData[ 'meals' ]; setState(() { _ideas = meals.map((json) => Lunch.fromJson(json)).toList(); }); } } } @override void initState() { super.initState(); _getLunchIdeas(); _centerController = ConfettiController(duration: const Duration(seconds: 10)); } @override void dispose() { selected.close(); _centerController.dispose(); super.dispose(); } var selectedIdea = "" ; late var selectedImg; void setValue(value) { selectedIdea = _ideas[value].meal.toString(); selectedImg = _ideas[value].img; } @override Widget build(BuildContext context) { var flag = false ; return Scaffold( appBar: AppBar( centerTitle: true , title: const Text( 'Gfg Lunch Wheel' ), ), body: _ideas.length > 2 ? Padding( padding: const EdgeInsets.all(8.0), child: GestureDetector( onTap: () { setState(() { selected.add( Fortune.randomInt(0, _ideas.length), ); }); }, child: Column( children: [ Expanded( child: FortuneWheel( selected: selected.stream, items: [ for (var it in _ideas) FortuneItem(child: Text(it.meal)), ], onAnimationEnd: () { _centerController.play(); showDialog( barrierDismissible: true , context: context, builder: (BuildContext context) { return Center( child: AlertDialog( scrollable: true , title: Text( "Hurray! today's meal is????" ), content: Column( children: [ ConfettiWidget( confettiController: _centerController, blastDirection: pi / 2, maxBlastForce: 5, minBlastForce: 1, emissionFrequency: 0.03, numberOfParticles: 10, gravity: 0), SizedBox(height: 10), Text( selectedIdea, style: TextStyle(fontSize: 22), ), SizedBox(height: 20), Image.network(selectedImg), ], ), ), ); }); }, onFocusItemChanged: (value) { if (flag == true ) { setValue(value); } else { flag = true ; } }, ), ), ], ), ), ) : Center( child: CircularProgressIndicator(color: Colors.green), )); } } |