Trivia Flutter App Project with Source Code
Flutter is a free and open-source UI framework from Google for building native mobile apps.
Contents
trivia_app
An trivia/quiz app made in flutter for answering fun trivia questions.
This app make use of Opentdb api (https://opentdb.com/) for fetching trivia questions.
What is Opentdb?
The Open Trivia Database is a user-contributed trivia database that is available for free. The Open Trivia Database API provides a set of trivia questions that may be filtered by category, difficulty, type, and encoding. The response is in JSON format.
Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
For help getting started with Flutter, view our online documentation, which offers tutorials, samples, guidance on mobile development, and a full API reference.
Source Code
- Create a flutter app using
flutter create trivia_app
command in the terminal. - Inside the
trivia_app
folder, go inside thelib
folder. Thelib
folder is where we place all the important stuff of our flutter application. - Inside the
lib
create the following .dart files and folder: lib/main.dart
import 'package:flutter/material.dart';
import 'package:trivia_app/first_page.dart';
import 'package:trivia_app/theme/theme_data.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: CustomTheme.appLightTheme(context),
darkTheme: CustomTheme.appDarkTheme(context),
debugShowCheckedModeBanner: false,
themeMode: ThemeMode.dark,
home: FirstPage(),
);
}
}
lib/api_trivia.dart
// import 'dart:developer';
// import 'package:http/http.dart' as http;
//
// Future<void> getStates() async {
// Uri url = Uri.parse("https://opentdb.com/api.php?amount=10");
//
// var response = await http.get(url);
// if (response.statusCode == 200) {
// log('api worked ${response.body}');
// var body = response.body;
// } else {
// log('api request failed ${response.body}');
//
// return null;
// }
// }
import 'dart:developer';
import 'dart:convert';
import 'package:trivia_app/model/questions.dart';
import 'package:http/http.dart' as http;
import 'package:trivia_app/model/questions.dart';
class ApiTrivia {
Future<List<Results>?> getStates() async {
Uri url = Uri.parse("https://opentdb.com/api.php?amount=10&difficulty=easy&type=multiple");
var response = await http.get(url);
if (response.statusCode == 200) {
log('api worked ${response.body}');
var body = response.body;
var statesJsonArray = json.decode(body)['results'];
try {
List<Results> results =
(statesJsonArray as List).map((e) => Results.fromJson(e)).toList();
return results;
} catch (e) {
log('try failed $e');
}
} else {
log('api request failed ${response.body}');
return null;
}
}
}
lib/first_page.dart
import 'dart:ui';
import 'package:google_fonts/google_fonts.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:trivia_app/second_page.dart';
class FirstPage extends StatefulWidget {
const FirstPage({Key? key}) : super(key: key);
@override
_FirstPageState createState() => _FirstPageState();
}
class _FirstPageState extends State<FirstPage> {
final ButtonStyle raisedButtonStyle = ElevatedButton.styleFrom(
primary: Colors.blueGrey[800],
minimumSize: Size(88, 36),
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 16),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(2)),
),
);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: MediaQuery.of(context).size.height * 0.45,
),
Text(
'TRIVIA APP',
textAlign: TextAlign.center,
style: GoogleFonts.rubik(
fontSize: 50,
fontWeight: FontWeight.w500,
),
),
SizedBox(
height: MediaQuery.of(context).size.height * 0.45,
width: 300,
child: Center(
child: ElevatedButton(
style: raisedButtonStyle,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondPage()),
);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'TAKE QUIZ',
),
SizedBox(
width: 10,
),
Icon(
Icons.arrow_forward_rounded,
),
],
),
),
),
),
],
),
),
);
}
}
void _ontapped() {}
lib/options.dart
import 'package:flutter/material.dart';
import 'package:trivia_app/question_pageview.dart';
import 'package:trivia_app/model/shuffleanswers.dart';
class Options extends StatefulWidget {
final List wrongRightList;
final OptionSelectedCallback onOptionsSelected;
final int selectedPosition;
final int index;
//List finalWrongrightlist = [];
Options({
required this.wrongRightList,
required this.onOptionsSelected,
required this.selectedPosition,
required this.index,
});
@override
_OptionsState createState() => _OptionsState();
}
class _OptionsState extends State<Options> {
int selectedIndex = 99;
@override
void initState() {
super.initState();
// Shuffleright a = Shuffleright(
// Shuffler: (list) {
// widget.finalWrongrightlist = list;
// },
// wrongright: widget.wrongright);
// a.mix();
selectedIndex = widget.selectedPosition;
print("positio here is $selectedIndex");
}
@override
Widget build(BuildContext context) {
return Expanded(
child: ListView.builder(
itemCount: widget.wrongRightList[widget.index].length,
itemBuilder: (context, position) {
return Card(
elevation: 2,
margin: EdgeInsets.symmetric(vertical: 6),
child: CheckboxListTile(
checkColor: Colors.blueGrey[800],
activeColor: Colors.blueGrey[100],
selectedTileColor: Colors.blueGrey[800],
selected: selectedIndex == position,
title: Text(
'${widget.wrongRightList[widget.index].elementAt(position)}'),
value: selectedIndex == position,
onChanged: (bool? newValue) {
widget.onOptionsSelected(
widget.wrongRightList[widget.index].elementAt(position));
setState(
() {
selectedIndex = position;
// widget.selectedPosition = position;
},
);
},
),
);
},
),
);
;
}
}
lib/question_pageview.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:trivia_app/options.dart';
import 'package:trivia_app/question_pageview.dart';
import 'package:trivia_app/scorepage.dart';
import 'package:trivia_app/model/shuffleanswers.dart';
typedef void OptionSelectedCallback(String option);
class QuestionsPageView extends StatefulWidget {
final List results;
final List wrongRightList;
QuestionsPageView({required this.results, required this.wrongRightList});
@override
_QuestionsPageViewState createState() => _QuestionsPageViewState();
}
class _QuestionsPageViewState extends State<QuestionsPageView> {
List<String> _userAnswerList = [];
List<String> correctanswerlist = [];
int currentPagePosition = 0;
PageController _controller = PageController();
@override
void initState() {
super.initState();
_userAnswerList.addAll(widget.results.map((e) => ""));
correctanswerlist = [];
for (int i = 0; i < widget.results.length; i++) {
correctanswerlist.add(widget.results.elementAt(i).correct_answer);
}
// for(int i =0; i < widget.results.length; i++) {
// _userAnswerList.add("");
// }
}
@override
Widget build(BuildContext context) {
final TextStyle subtitle = Theme.of(context).textTheme.subtitle1!;
final TextStyle body = Theme.of(context).textTheme.bodyText1!;
return SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
width: MediaQuery.of(context).size.width * 0.9,
height: MediaQuery.of(context).size.height * 0.7,
child: Center(
child: PageView.builder(
controller: _controller,
itemCount: widget.results.length,
pageSnapping: true,
onPageChanged: (position) {
currentPagePosition = position;
},
itemBuilder: (context, index) {
String userAnswer = _userAnswerList[index];
int checkedOptionPosition =
widget.wrongRightList[index].indexOf(userAnswer);
return Container(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 18.0, vertical: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 10,
),
Text(
'Question ${index + 1}',
style: GoogleFonts.oswald(
textStyle: subtitle, color: Colors.yellow[800]),
),
SizedBox(
height: 15,
),
Text(
'${widget.results.elementAt(index).question}',
style:
GoogleFonts.lato(textStyle: body, fontSize: 20),
),
SizedBox(
height: 20,
),
Options(
index: index,
wrongRightList: widget.wrongRightList,
selectedPosition: checkedOptionPosition,
onOptionsSelected: (selectedOption) {
print("selected item is $selectedOption");
_userAnswerList[currentPagePosition] =
selectedOption;
print(_userAnswerList.toList().toString());
},
),
SizedBox(
height: 60,
)
],
),
),
);
},
),
),
),
Center(
child: Container(
width: MediaQuery.of(context).size.width * 0.9,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: [
TextButton(
style: ElevatedButton.styleFrom(
padding: EdgeInsets.all(20),
),
onPressed: () {
_controller.animateToPage(
currentPagePosition == 0
? 0
: currentPagePosition - 1,
duration: Duration(milliseconds: 100),
curve: Curves.easeIn,
);
},
child: Text(
'Previous',
style: GoogleFonts.lato(
textStyle: body,
fontSize: 15,
color: Colors.yellow[800]),
),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
padding: EdgeInsets.all(15),
primary: Colors.yellow[800],
),
onPressed: () {
_controller.animateToPage(
currentPagePosition == widget.results.length - 1
? currentPagePosition
: currentPagePosition + 1,
duration: Duration(milliseconds: 100),
curve: Curves.easeIn);
},
child: Text(
'Next',
style: GoogleFonts.lato(
textStyle: body,
fontWeight: FontWeight.bold,
fontSize: 15,
color: Colors.blueGrey[800]),
),
),
],
),
SizedBox(
height: 25,
),
ElevatedButton(
style: ElevatedButton.styleFrom(
padding: EdgeInsets.all(10),
primary: Colors.blueGrey[800],
fixedSize:
Size(MediaQuery.of(context).size.width * 0.7, 50)),
onPressed: () {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => ScorePage(
useranswerlist: _userAnswerList,
correctanswerlist: correctanswerlist,
),
),
(route) => false);
},
child: Text(
'SUBMIT',
style: GoogleFonts.lato(
textStyle: body,
fontSize: 17,
),
),
),
],
),
),
)
],
),
);
}
}
lib/scorepage.dart
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:trivia_app/second_page.dart';
class ScorePage extends StatefulWidget {
final List useranswerlist;
final List correctanswerlist;
const ScorePage(
{Key? key, required this.useranswerlist, required this.correctanswerlist})
: super(key: key);
@override
_ScorePageState createState() => _ScorePageState();
}
class _ScorePageState extends State<ScorePage> {
int count = 0;
int total = 0;
@override
void initState() {
for (int i = 0; i < widget.correctanswerlist.length; i++) {
if (widget.correctanswerlist.elementAt(i) ==
widget.useranswerlist.elementAt(i)) {
count++;
}
}
count *= 10;
total = widget.correctanswerlist.length * 10;
}
@override
Widget build(BuildContext context) {
final TextStyle body = Theme.of(context).textTheme.bodyText1!;
return Scaffold(
appBar: AppBar(
elevation: 0.0,
backgroundColor: Colors.transparent,
centerTitle: true,
title: Text('Trivia Result'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.all(48),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.blueGrey[800],
boxShadow: [
BoxShadow(
color: Colors.white.withAlpha(60),
blurRadius: 6.0,
spreadRadius: 4.0,
),
]),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Center(
child: Text(
"Score",
style: GoogleFonts.lato(textStyle: body, fontSize: 30),
),
),
Center(
child: Text(
'${count} / ${total}',
style: GoogleFonts.lato(textStyle: body, fontSize: 28),
),
),
],
),
),
SizedBox(
height: 90,
),
Container(
width: 200,
child: Center(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
padding: EdgeInsets.all(20),
primary: Colors.blueGrey[800],
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondPage(),
),
);
},
child: Text(
'Retake Test',
style: GoogleFonts.lato(textStyle: body, fontSize: 20),
),
),
),
),
SizedBox(
height: 40,
)
],
));
}
}
lib/second_page.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:trivia_app/api_trivia.dart';
import 'package:trivia_app/model/questions.dart';
import 'dart:math';
import 'package:trivia_app/question_pageview.dart';
import 'package:trivia_app/model/shuffleanswers.dart';
//todo change this name
typedef void Randomise(List options);
class SecondPage extends StatefulWidget {
SecondPage({Key? key}) : super(key: key);
List wrongRightList = [];
@override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
final ApiTrivia _apitrivia = ApiTrivia();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0.0,
backgroundColor: Colors.transparent,
centerTitle: true,
title: const Text('Trivia questions'),
),
body: _futureWidget(),
);
}
_futureWidget() {
return FutureBuilder(
future: _apitrivia.getStates(),
builder: (context, snapshot) {
if (snapshot.hasData) {
List results = snapshot.data as List;
ShuffleRight(
result: results,
Shuffler: (options) {
widget.wrongRightList = options;
});
return QuestionsPageView(
results: results, wrongRightList: widget.wrongRightList);
} else {
return Center(child: CircularProgressIndicator());
}
},
);
}
}
lib/model/questions.dart
class Results {
String? question;
String? correct_answer;
List<dynamic> incorrect_answers;
Results({this.question, this.correct_answer, required this.incorrect_answers});
Results.fromJson(Map<String, dynamic> mapOfJson)
: question = mapOfJson["question"],
correct_answer = mapOfJson["correct_answer"],
incorrect_answers = mapOfJson["incorrect_answers"];
}
lib/model/shuffleanswers.dart
import 'package:trivia_app/question_pageview.dart';
import 'package:flutter/material.dart';
import 'package:trivia_app/second_page.dart';
import '../options.dart';
class ShuffleRight {
final List result;
final Randomise Shuffler;
final List wrongRightList = [];
ShuffleRight({required this.result, required this.Shuffler}) {
wrongRightList.addAll(result.map((e) => []));
for (int i = 0; i < result.length; i++) {
List wrong = result.elementAt(i).incorrect_answers;
List right = [result.elementAt(i).correct_answer];
wrongRightList[i] = wrong + right;
wrongRightList[i].shuffle();
}
Shuffler(wrongRightList);
}
}
lib/theme/theme_data.dart
import 'package:flutter/material.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
class CustomTheme {
static ThemeData appLightTheme(BuildContext context) {
return ThemeData(
brightness: Brightness.light,
// textTheme: GoogleFonts.(Theme.of(context).textTheme)
);
}
static ThemeData appDarkTheme(BuildContext context) {
return ThemeData(
brightness: Brightness.dark,
// textTheme: GoogleFonts.latoTextTheme(),
);
}
}
Inside the App
GitHub Repository
Source Code: trivia_app.