I am new to flutter, I have built a quizz app that takes 5 questions randomly from a pool of questions and presents them to the user one after the other, then displays the total score at the end (on a different) screen with the option of retaking the quiz (with another set of randomly picked questions).
My issue I am facing is that when I choose to retake the quiz, if in the pool of questions presented there is a question from the past quiz, it still has its options highlighted (marked either wrong or correct as per the previous selection).
Can someone help me on how to totally dismiss previous choices after taking a quiz ?
This is an example of question answered in the previous quiz, and it came back with the option already highlighted (my previous answer).
[enter image description here][1]
[1]: https://i.stack.imgur.com/U1YFf.png[enter image description here][1]
Here is my code:
import 'package:flutter/material.dart';
import 'package:percent_indicator/percent_indicator.dart';
import 'package:schoolest_app/widgets/quizz/quizz_image_container.dart';
import '../../models/quizz.dart';
import '../../widgets/quizz/options_widget.dart';
import '../../widgets/quizz/quizz_border_container.dart';
import '../../widgets/quizz/result_page.dart';
class QuizzDisplayScreen extends StatefulWidget {
const QuizzDisplayScreen({
Key? key,
}) : super(key: key);
static const routeName = '/quizz-display';
#override
State<QuizzDisplayScreen> createState() => _QuizzDisplayScreenState();
}
class _QuizzDisplayScreenState extends State<QuizzDisplayScreen> {
enter code here
late String quizzCategoryTitle;
late List<Question> categoryQuestions;
late List<Question> quizCategoryQuestions;
var _loadedInitData = false;
#override
void didChangeDependencies() {
if (!_loadedInitData) {
final routeArgs =
ModalRoute.of(context)!.settings.arguments as Map<String, String>;
quizzCategoryTitle = (routeArgs['title']).toString();
// final categoryId = routeArgs['id'];
categoryQuestions = questions.where((question) {
return question.categories.contains(quizzCategoryTitle);
}).toList();
quizCategoryQuestions =
(categoryQuestions.toSet().toList()..shuffle()).take(5).toList();
_loadedInitData = true;
}
super.didChangeDependencies();
}
late PageController _controller;
int _questionNumber = 1;
int _score = 0;
int _totalQuestions = 0;
bool _isLocked = false;
void _resetQuiz() {
for (var element in quizCategoryQuestions) {
setState(()=> element.isLocked == false);
}
}
#override
void initState() {
super.initState();
_controller = PageController(initialPage: 0);
}
#override
void dispose() {
_controller.dispose();
_resetQuiz();
super.dispose();
}
#override
Widget build(BuildContext context) {
final myPrimaryColor = Theme.of(context).colorScheme.primary;
final mySecondaryColor = Theme.of(context).colorScheme.secondary;
double answeredPercentage =
(_questionNumber / quizCategoryQuestions.length);
return quizCategoryQuestions.isEmpty
? Scaffold(
appBar: AppBar(
title: Text(
'Quizz - $quizzCategoryTitle',
style: TextStyle(color: myPrimaryColor),
),
iconTheme: IconThemeData(
color: myPrimaryColor,
),
centerTitle: true,
backgroundColor: Colors.transparent,
elevation: 0,
flexibleSpace: Container(
decoration: BoxDecoration(`enter code here`
borderRadius: const BorderRadius.only(`enter code here`
bottomLeft: Radius.circular(15),
bottomRight: Radius.circular(15),
),
color: mySecondaryColor,
border: Border.all(color: myPrimaryColor, width: 1.0),
),
),
),
body: const Center(
child: Text('Cette catégorie est vide pour l\'instant'),
))
: Scaffold(
appBar: AppBar(
title: Text(
'Quizz - $quizzCategoryTitle',
style: TextStyle(color: myPrimaryColor),
),
iconTheme: IconThemeData(
color: myPrimaryColor,
),
centerTitle: true,
backgroundColor: Colors.transparent,
elevation: 0,
flexibleSpace: Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(15),
bottomRight: Radius.circular(15),
),
color: mySecondaryColor,
border: Border.all(color: myPrimaryColor, width: 1.0),
),
),
),
body: Container(
// height: 600,
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
children: [
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(
'Question $_questionNumber/${quizCategoryQuestions.length}',
style: const TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
),
CircularPercentIndicator(
radius: 40,
// animation: true,
// animationDuration: 2000,
percent: answeredPercentage,
progressColor: myPrimaryColor,
backgroundColor: Colors.cyan.shade100,
circularStrokeCap: CircularStrokeCap.round,
center: Text(
// ignore: unnecessary_brace_in_string_interps
'${(answeredPercentage * 100).round()} %',
style: const TextStyle(
fontSize: 10, fontWeight: FontWeight.bold),
),
// lineWidth: 10,
)
],
),
const SizedBox(height: 10),
Divider(
thickness: 1,
color: myPrimaryColor,
),
Expanded(
child: PageView.builder(
itemCount: quizCategoryQuestions.length,
controller: _controller,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
final _question = quizCategoryQuestions[index];
return buildQuestion(_question);
},
),
),
_isLocked
? buildElevatedButton(context)
: const SizedBox.shrink(),
const SizedBox(height: 10),
],
),
),
);
}
Column buildQuestion(Question question) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 10),
question.text!.isNotEmpty
? QuizzBorderContainer(
childWidget: Text(
question.text!,
style: const TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
),
)
: const SizedBox.shrink(),
question.imagePath!.isNotEmpty
? QuizzImageContainer(imagePath: question.imagePath!)
: const SizedBox.shrink(),
Expanded(
child: OptionsWidget(
question: question,
onClickedOption: (option) {
if (question.isLocked) {
return;
} else {
setState(() {
question.isLocked = true;
question.selectedOption = option;
});
_isLocked = question.isLocked;
if (question.selectedOption!.isCorrect) {
_score++;
}
}
},
),
),
],
);
}
ElevatedButton buildElevatedButton(BuildContext context) {
final mySecondaryColor = Theme.of(context).colorScheme.secondary;
return ElevatedButton(
onPressed: () {
if (_questionNumber < quizCategoryQuestions.length) {
_controller.nextPage(
duration: const Duration(milliseconds: 1000),
curve: Curves.easeInExpo,
);
setState(() {
_questionNumber++;
_isLocked = false;
});
} else {
setState(() {
// _isLocked = false;
_totalQuestions = quizCategoryQuestions.length;
});
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) =>
ResultPage(score: _score, totalQuestions: _totalQuestions),
),
);
}
},
child: Text(
_questionNumber < quizCategoryQuestions.length
? 'Suivant'
: 'Voir le résultat',
style: TextStyle(
color: mySecondaryColor,
fontWeight: FontWeight.bold,
),
),
);
}
}
I don't seem to the solution to this.
And this is the code on the result page:
import 'package:flutter/material.dart';
import '../../screens/quizz/quizz_screen.dart';
class ResultPage extends StatefulWidget {
final int score;
final int totalQuestions;
const ResultPage({
Key? key,
required this.score,
required this.totalQuestions,
}) : super(key: key);
#override
State<ResultPage> createState() => _ResultPageState();
}
class _ResultPageState extends State<ResultPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SizedBox(
height: 150,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
'You got ${widget.score}/${widget.totalQuestions}',
style: const TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const QuizzScreen(),
),
);
},
child: const Text('OK'),
),
],
),
),
),
);
}
}
I don't know what is missing to get the reset right.
When you want to take retest try to dispose all the answers which are saved in the memory. Or you can use these navigators to which might help you in solving the issue. Try using pushReplacement or pushAndRemoveUntil when navigating to retest, this will clear the memory of last pages and you will achive the goal which you want.
Related
I'm new to Flutter and I'm having problems with expansion panel lists. Firstly, let me explain what I'm trying to do. I have a map application that gets the user's current location when they press the "start" button and displays the distance, date and time when the journey is finished and the "finish" button is pressed. Then, this information is stored in an expansion panel list on a separate "history" page.
However, I also have a third button called "add location". This button appears when the "start" button is pressed and allows the user to add their current location while the journey is ongoing. These locations are added to a list called "tripLocations" within my code. Then, when the "finish" button is pressed, all of this information is added to the expansion panel list.
My problem is that every time a journey is finished, the "tripLocations" list is displayed in the expansion panel list and shows all of the previous locations. However, I want each row in the expansion panel list to represent a single journey and only show the locations that were added during that journey. Essentially, there should be a "tripLocations" list for each row in the expansion panel list and it should be reset every time a new journey is started.
The following codes belong to my button.dart page.
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:material_dialogs/shared/types.dart';
import 'package:material_dialogs/widgets/buttons/icon_outline_button.dart';
import 'package:yolumukaydet/constant/constant.dart';
import 'package:latlong/latlong.dart';
import 'package:material_dialogs/material_dialogs.dart';
import 'package:lottie/lottie.dart';
import 'package:material_dialogs/widgets/buttons/icon_button.dart';
import 'package:geocoding/geocoding.dart';
import 'package:geocoding_platform_interface/geocoding_platform_interface.dart';
class GradientText extends StatelessWidget {
const GradientText(
this.text, {
required this.gradient,
this.style,
});
final String text;
final TextStyle? style;
final Gradient gradient;
#override
Widget build(BuildContext context) {
return ShaderMask(
blendMode: BlendMode.srcIn,
shaderCallback: (bounds) => gradient.createShader(
Rect.fromLTWH(0, 0, bounds.width, bounds.height),
),
child: Text(text, style: style),
);
}
}// texte gradient atamak için yapıldı
class StartHistory{
String city1;
String district1;
bool isExpanded;
StartHistory({
required this.city1,
required this.district1,
this.isExpanded = false,});
}//varış yeri konumu gösterme
class StartHistoryData {
static List<StartHistory> starthistory = [];
static void addStartHistory(String city1, String district1) {
starthistory.add(StartHistory(city1: city1, district1: district1));
}
}//varış yeri konumu gösterme
class TripLocation {
String address;
TripLocation({
required this.address,
});
}
class History {
String distance;
String date;
String time;
String city;
String district;
bool isExpanded;
History({
required this.distance,
required this.date,
required this.time,
required this.city,
required this.district,
this.isExpanded = false,
});
}
class HistoryData {
static List<History> history = [];
static List<TripLocation> tripLocations = [];
static void addHistory(String distance, String date, String time, String city, String district,) {
history.add(History(distance: distance, date: date, time: time, city: city, district: district,));
}
static void addTripLocation(TripLocation tripLocation) {
tripLocations.add(tripLocation);
}
}//mesafe ,tarih ve saat bilgileri
class Location {
final double latitude;
final double longitude;
final String note;
Location({
required this.latitude,
required this.longitude,
required this.note,
});
}//Konum Ekle butonu için konum alıyor
TextEditingController _noteController = TextEditingController();
List<Map<String, String>> _locations = [];
class ImageButton extends StatefulWidget {
#override
_ImageButtonState createState() => _ImageButtonState();
}
class _ImageButtonState extends State<ImageButton>with TickerProviderStateMixin {
bool _isStarted = false;
bool _isStopped = true;
void _getCurrentLocation() async {
final position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.best);
setState(() {
if (position != null) {
_currentPosition = position;
}
});
}
late Position _startPosition;
late Position _currentPosition;
double _distance = 0.0;
String _time = "";
Geolocator geolocator = Geolocator();
int _selectedIndex = 0;
final List<String> imageList = [
'assets/images/pwb3.png',
'assets/images/pwb4.png',
];
void _onPressed1() {
setState(() {
_selectedIndex = (_selectedIndex + 1) % imageList.length;
});
// buraya butona tıklama olayında yapmanız gereken diğer işlemleri ekleyebilirsiniz
}
late AnimationController _controller;
bool isOpened = false;
#override
void initState() {
_getCurrentLocation();
_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 400),
);
super.initState();
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
child: Align(
child: Stack(
children: <Widget>[
Container(
margin: EdgeInsets.only(left:160,top: 540.0,right:150),
width: 350.0,
height: 350.0,
child: FloatingActionButton(
child: Image.asset(
imageList[_selectedIndex],
height: 350.0,
width: 350.0,
),
onPressed: () {
setState(() {
_onPressed1();
isOpened = !isOpened;
if (isOpened) {
_controller.forward();
} else {
_controller.reverse();
}
});
},
),
),
Visibility(
visible: _isStarted,
child: Align(
alignment: Alignment.topRight,
child: Container(
width: 75.0,
height: 85.0,
margin: EdgeInsets.only(top: 540.0,right:50),
child: ScaleTransition(
scale: _controller,
child: FloatingActionButton(
backgroundColor: Constant.green,
child: Icon(Icons.add,size: 40.0,color: Constant.darkgrey,),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
String noteText = '';
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
child: Container(
padding: EdgeInsets.all(20.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Konumunuz Eklenecek',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.0,
),
),
SizedBox(height: 10.0),
Divider(
color: Colors.black,
height: 1.0,
),
SizedBox(height: 10.0),
Text(
'Eklediğiniz konum ve notlar geçmiş sayfasına kaydedilecektir.',
style: TextStyle(
fontSize: 8.0,
),
),
SizedBox(height: 10.0),
TextField(
controller: _noteController,
decoration: InputDecoration(hintText: 'Notunuzu Ekleyin'),
),
SizedBox(height: 10.0),
ElevatedButton(
onPressed: () async {
_getCurrentLocation();
//BURASI İL VE İLÇE KONUM ADLARINI ALIYOR VE CİTY İLE DİSTİRCT DEĞİŞKENİN ATIYOR
List<Placemark> placemarks = await placemarkFromCoordinates(_currentPosition.latitude, _currentPosition.longitude);
String _address = '${placemarks[0].administrativeArea}, ${placemarks[1].subAdministrativeArea}';
var tripLocation = TripLocation(address: _address);
HistoryData.addTripLocation(tripLocation);
Navigator.pop(context);
setState(() {});
print("tripLocation");
},
child: Text('Tamam'),
),
],
),
),
);
},
);
},
),
),
),
)),// KONUM EKLE BUTTONU
Visibility(
visible: _isStarted,
child: Align(
alignment: Alignment.topLeft,
child: Container(
width: 75.0,
height: 75.0,
margin: EdgeInsets.only(left:50,top: 540.0,),
child: ScaleTransition(
scale: _controller,
child: FloatingActionButton(
backgroundColor: Constant.green,
child: Icon(Icons.stop,size:40.0,color: Constant.darkgrey,),
onPressed: () async {
setState(() {
_isStopped = true;
_isStarted = false;
});
//BURASI İL VE İLÇE KONUM ADLARINI ALIYOR VE CİTY İLE DİSTİRCT DEĞİŞKENİN ATIYOR
List<Placemark> placemarks = await placemarkFromCoordinates(_currentPosition.latitude, _currentPosition.longitude);
String? city = placemarks.isNotEmpty ? placemarks[0].administrativeArea: '';
String? district = placemarks.isNotEmpty ? placemarks[1].subAdministrativeArea : '';
//
//
_currentPosition = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.best);
final Distance distance = new Distance();
var km = distance.as(
LengthUnit.Kilometer,
new LatLng(_startPosition.latitude, _startPosition.longitude),
new LatLng(_currentPosition.latitude,_currentPosition.longitude));
setState(() {
_time = DateTime.now().toString().substring(0, 19);
});
setState(() {
_distance = km as double;
});
showDialog(
context: context,
builder: (context) {
return AlertDialog(
backgroundColor: Constant.darkgrey,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50.0),
),
title: Container(
alignment: Alignment.center,
child: Text('Mesafe Bilgisi',
style: TextStyle(
color: Colors.white
),)),
content: Container(
height: 400,
width: 400,
decoration: BoxDecoration(
color: Constant.darkgrey,
borderRadius: BorderRadius.circular(50.0),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
GradientText(
'$_distance km',
style: const TextStyle(fontSize: 80,
fontFamily: 'digital',
),
gradient: LinearGradient(colors: [
Colors.white54,
Colors.white70,
]),
),
Text("Tarih ${DateTime.now().toString().split(' ').first} / SAAT ${DateTime.now().toString().split(' ').last.substring(0, 8)}",
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.white
),
),
Text(
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.white
),"$city / $district"),
Container(
width:350,
height: 45,
child: FloatingActionButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(5),
topRight: Radius.circular(5),
bottomLeft: Radius.circular(5),
bottomRight: Radius.circular(5),
),
),
backgroundColor: Constant.green,
child: Text("TAMAM",
style: TextStyle(
color: Constant.darkgrey,
),),
onPressed: () {
HistoryData.addHistory("$_distance km", "${DateTime.now().toString().split(' ').first}", "${DateTime.now().toString().split(' ').last.substring(0, 8)}","$city","$district",);
Navigator.of(context).pop();
},
),
),
],
),
),
);
},
);
},
),
),
),
),
),// STOP BUTTONU
Visibility(
visible: _isStopped,
child: Align(
alignment: Alignment.topCenter,
child: Container(
width: 75.0,
height: 75.0,
margin: EdgeInsets.only(top: 440.0),
child: ScaleTransition(
scale: _controller,
child: FloatingActionButton(
backgroundColor: Constant.green,
child: Icon(Icons.start,size:40.0,color: Constant.darkgrey,),
onPressed: () async {
List<Placemark> placemarks = await placemarkFromCoordinates(_currentPosition.latitude, _currentPosition.longitude);
String? city1 = placemarks.isNotEmpty ? placemarks[0].administrativeArea: '';
String? district1 = placemarks.isNotEmpty ? placemarks[1].subAdministrativeArea : '';
StartHistoryData.addStartHistory("$city1","$district1");
setState(() {
_isStarted = true;
_isStopped = false;
});
_startPosition = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.best);
},
),
),
),
),) // START BUTTONU
],
),
),
);
}
}
These codes belong to my historypage.dart file
import 'dart:ffi';
import 'package:flutter/material.dart';
import 'package:yolumukaydet/appbar.dart';
import 'package:yolumukaydet/constant/constant.dart';
import 'package:yolumukaydet/menu.dart';
import 'package:yolumukaydet/navigator_bar.dart';
import 'package:flutter/material.dart';
import 'package:yolumukaydet/button.dart';
class GecmisPage extends StatefulWidget {
#override
_GecmisPageState createState() => _GecmisPageState();
}
class _GecmisPageState extends State<GecmisPage> {
List<Map<String, dynamic>> _locations = [];
void _removeItem(String date) {
int indexToRemove = HistoryData.history.indexWhere((element) =>
element.date == date);
setState(() {
HistoryData.history.removeAt(indexToRemove);
});
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
duration: const Duration(milliseconds: 600),
content: Text('#$date tarihli bilginiz silindi.')));
} // delete tuşu
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Constant.darkgrey,
appBar: AppBarPage(),
body: Column(
children: [
ExpansionPanelList(
expansionCallback: (int index, bool isExpanded) {
setState(() {
HistoryData.history[index].isExpanded = !isExpanded;
});
},
children: List.generate(
HistoryData.history.length,
(index) {
return ExpansionPanel(
backgroundColor: Constant.opacgrey,
headerBuilder: (BuildContext context, bool isExpanded) {
return ListTile(
title: Text("Tarih: " + HistoryData.history[index].date + " - " + HistoryData.history[index].city + "/" + HistoryData.history[index].district),
leading: IconButton(
onPressed: () => _removeItem(HistoryData.history[index].date),
icon: const Icon(
Icons.delete,
color: Constant.green,
),
),
);
},
body: Container(
height: 500,
width: double.infinity,
color: Constant.opacgrey,
child: Container(
height: 250,
width: double.infinity,
decoration: BoxDecoration(),//boş bırakıldı
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Mesafe :", style: TextStyle(fontSize: 18.0)),
Text("${HistoryData.history[index].distance}",
style: TextStyle(fontSize: 18.0)),
Text(" / ", style: TextStyle(fontSize: 18.0)),
Text("Saat :", style: TextStyle(fontSize: 18.0)),
Text("${HistoryData.history[index].time}",
style: TextStyle(fontSize: 18.0)),
],
),
),
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Varış Yeri :", style: TextStyle(fontSize: 18.0)),
Text("${StartHistoryData.starthistory[index].city1}",
style: TextStyle(fontSize: 18.0)),
Text(" / ", style: TextStyle(fontSize: 18.0)),
Text("${StartHistoryData.starthistory[index].district1}",
style: TextStyle(fontSize: 18.0)),
],
),
),
Container(
height: 400,
width: double.infinity,
decoration: BoxDecoration(
border: Border.all(color: Colors.black,width: 2)
),
child:
ListView.builder(
itemCount: HistoryData.tripLocations.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(HistoryData.tripLocations[index].address),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
setState(() {
HistoryData.tripLocations.removeAt(index);
});
},
),
);
},
)
),
],
),
),
),
),
isExpanded: HistoryData.history[index].isExpanded,
);
},
),
),
],
),
);
}
}
Where should I make the corrections here? I'm waiting for your help, thank you.
I'm using animated containers and I want them to change color when I use NotifyListeners();
Thing is, the color only updates when you click on the container, otherwise it remains the same.
I tried switching to normal containers, I tried changing the home screen to stateful, nothing seems to work, any help is appreciated.
Github link: https://github.com/amrogad/beat_maker
void main() {
runApp(MultiProvider(providers: [
ChangeNotifierProvider<SoundProvider>(create: (_) => SoundProvider(),
),
ChangeNotifierProvider<IconProvider>(create: (_) => IconProvider(),
),
], child: MyApp()));
}
class IconProvider extends ChangeNotifier {
Color primary1 = Colors.deepPurpleAccent;
Color secondary1 = Colors.deepPurple;
Color primary2 = Colors.yellowAccent;
Color secondary2 = Colors.orangeAccent;
Color primary3 = Colors.cyanAccent;
Color secondary3 = Colors.blue;
Color primary4 = Colors.lime;
Color secondary4 = Colors.lightGreen;
void changeIcons(
Color primaryPath1,
Color secondaryPath1,
Color primaryPath2,
Color secondaryPath2,
Color primaryPath3,
Color secondaryPath3,
Color primaryPath4,
Color secondaryPath4,
) {
primary1 = primaryPath1;
secondary1 = secondaryPath1;
primary2 = primaryPath2;
secondary2 = secondaryPath2;
primary3 = primaryPath3;
secondary3 = secondaryPath3;
primary4 = primaryPath4;
secondary4 = secondaryPath4;
notifyListeners();
}
}
class HomeScreen extends StatelessWidget {
static const String routeName = 'Home Screen';
#override
Widget build(BuildContext context) {
var soundProvider = Provider.of<SoundProvider>(context);
return SafeArea(
child: Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
automaticallyImplyLeading: false,
backgroundColor: Colors.black,
title: Text(
'Beat Maker',
style: GoogleFonts.anton(color: Colors.lime, fontSize: 24),
),
centerTitle: true,
actions: [
GestureDetector(
onTap: () {
Navigator.of(context).pushNamed(SettingsScreen.routeName);
},
child: Icon(
Icons.settings,
color: Colors.lime,
size: 24,
)),
SizedBox(
width: 10,
),
],
),
body: Consumer<IconProvider>(
builder: (context, icon, child) {
return GridView(
physics: NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4
),
children: [
SoundButton(
icon.primary1, icon.secondary1, soundProvider.note1),
SoundButton(
icon.primary2, icon.secondary2, soundProvider.note2),
SoundButton(
icon.primary3, icon.secondary3, soundProvider.note3),
SoundButton(
icon.primary4, icon.secondary4, soundProvider.note4)
],
);
}
),
),
);
}
class SoundButton extends StatefulWidget {
final Color mainColor;
final Color sideColor;
final note;
const SoundButton(this.mainColor, this.sideColor, this.note, {Key? key})
: super(key: key);
#override
State<SoundButton> createState() => _SoundButtonState();
}
class _SoundButtonState extends State<SoundButton> {
late Color _mainColor;
late Color _sideColor;
final player = AudioPlayer();
#override
void initState() {
_mainColor = widget.mainColor;
_sideColor = widget.sideColor;
super.initState();
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
setState(() {
_mainColor = Colors.redAccent;
_sideColor = Colors.red;
player.play(AssetSource(widget.note));
});
},
child: AnimatedContainer(
decoration: BoxDecoration(
border: Border.all(width: 1),
gradient: RadialGradient(colors: [_mainColor, _sideColor]),
boxShadow: const [
BoxShadow(
blurRadius: 1.0,
spreadRadius: 1.0,
offset: Offset(
1.0,
1.0,
),
),
]),
curve: Curves.easeOutCubic,
onEnd: () => Reset(),
duration: const Duration(milliseconds: 100),
),
);
}
void Reset() {
setState(() {
_mainColor = widget.mainColor;
_sideColor = widget.sideColor;
});
}
}
class OpenIconBottomSheet extends StatelessWidget {
const OpenIconBottomSheet({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
var iconProvider = Provider.of<IconProvider>(context);
return Container(
height: 106,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(12), topRight: Radius.circular(12)),
color: Colors.black,
border: Border.all(width: 3, color: Colors.lime)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
InkWell(
onTap: () {
iconProvider.changeIcons(
Colors.deepPurpleAccent,
Colors.deepPurple,
Colors.yellowAccent,
Colors.orangeAccent,
Colors.cyanAccent,
Colors.blue,
Colors.lime,
Colors.lightGreen,
);
Navigator.pop(context);
},
child: isSelected(
'Main Theme',
iconProvider.primary1 == Colors.deepPurpleAccent
? true
: false)),
Container(
width: double.infinity,
height: 3,
color: Colors.lime,
),
SizedBox(
height: 7,
),
InkWell(
onTap: () {
iconProvider.changeIcons(
Colors.white70,
Colors.white,
Colors.pinkAccent,
Colors.pink,
Colors.tealAccent,
Colors.teal,
Colors.amberAccent,
Colors.redAccent,
);
Navigator.pop(context);
},
child: isSelected('Secondary Theme',
iconProvider.primary1 == Colors.white70 ? true : false)),
],
),
);
}
Widget isSelected(String text, bool selected) {
if (selected) {
return Padding(
padding: EdgeInsets.all(6),
child: Text(text,
style: GoogleFonts.anton(color: Colors.lime, fontSize: 24)),
);
} else {
return Padding(
padding: EdgeInsets.all(6),
child: Text(text,
style: GoogleFonts.anton(color: Colors.white, fontSize: 24)),
);
}
}
}
I'm emulating this search and filter github here and the codes are almost the same but the filtered results do not update instantly while I type and also I faced the following issues:
I will have to press enter on my laptop to finally get the filtered list
When I hit the close icon(which is to clear all the words), I will have to tap the searchbar again so that all my listtile are back on the listview.
Here's my code:
class _CurrencySelectState extends State<CurrencySelect> {
late List<Currency> resCur;
String query = '';
#override
void initState() {
super.initState();
resCur = currencyList;
}
void searchCur(String query) {
final List<Currency> filteredCur = currencyList.where((cur) {
final symbolLower = cur.symbol.toLowerCase(); // Search using symbol
final nameLower = cur.country.toLowerCase(); // Search using country
final searchLower = query.toLowerCase();
return symbolLower.contains(searchLower) ||
nameLower.contains(searchLower);
}).toList();
setState(() {
this.query = query;
resCur = filteredCur;
});
}
#override
Widget build(BuildContext context) {
Widget buildCur(Currency cur) => ListTile(
leading: Padding(
padding: EdgeInset.all(5)
child: SizedBox(
child: Column(
children: <Widget>[
SvgPicture.asset(
cur.assetPath,
),
]),
),
),
title: Column(
children: [
Text(
cur.symbol,
style: TextStyle(
...
),
Text(
cur.name,
style: TextStyle(
...
),
],
),
trailing: Text(
"0.25",
style: TextStyle(
...
),
);
return TextButton(
onPressed: () async {
showModalBottomSheet(
enableDrag: false,
context: context,
isScrollControlled: true,
builder: (BuildContext context) {
return DraggableScrollableSheet(
expand: false,
builder: (context, scrollController) {
return Column(
children: <Widget>[
SearchWidget(
text: query,
onChanged: searchCur,
hintText: "Enter symbol or country"
),
Expanded(
child: ListView.builder(
controller: scrollController,
itemCount: resCur.length,
itemBuilder: (context, int index) {
final cur = resCur[index];
return buildCur(cur);
},
),
)
],
);
},
);
});
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(
...
),
SvgPicture.asset(
...
)
],
));
}
}
Searchwidget code:
import 'package:flutter/material.dart';
class SearchWidget extends StatefulWidget {
final String text;
final ValueChanged<String> onChanged;
final String hintText;
const SearchWidget({
Key? key,
required this.text,
required this.onChanged,
required this.hintText,
}) : super(key: key);
#override
_SearchWidgetState createState() => _SearchWidgetState();
}
class _SearchWidgetState extends State<SearchWidget> {
final controller = TextEditingController();
#override
Widget build(BuildContext context) {
final styleActive = TextStyle(color: Colors.black);
final styleHint = TextStyle(color: Colors.black54);
final style = widget.text.isEmpty ? styleHint : styleActive;
return Container(
height: 42,
margin: const EdgeInsets.fromLTRB(16, 16, 16, 16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Colors.white,
border: Border.all(color: Colors.black26),
),
padding: const EdgeInsets.symmetric(horizontal: 8),
child: TextField(
controller: controller,
decoration: InputDecoration(
icon: Icon(Icons.search, color: style.color),
suffixIcon: widget.text.isNotEmpty
? GestureDetector(
child: Icon(Icons.close, color: style.color),
onTap: () {
controller.clear();
widget.onChanged('');
FocusScope.of(context).requestFocus(FocusNode());
},
)
: null,
hintText: widget.hintText,
hintStyle: style,
border: InputBorder.none,
),
style: style,
onChanged: widget.onChanged,
),
);
}
}
I am building a quiz app and I created a custom widget to save me a lot of time as I have a lot of questions for the quiz. Everything works apart from the scoring system. If I create multiple instances of the same widget the score will not be incremented and it will stay on 1. Is there any way I can pass each score of the widgets to a global variable in my main widget so then I can add all the scores? (I'm new to flutter).
Custom Widget
class Questions extends StatefulWidget {
final String imagePath;
final String question;
final String answer1;
final String answer2;
final String answer3;
final String answer4;
final bool iscorrectAnswer1;
final bool iscorrectAnswer2;
final bool iscorrectAnswer3;
final bool iscorrectAnswer4;
int score = 0;
bool questionsAnswered = false;
Questions(
this.imagePath,
this.question,
this.answer1,
this.answer2,
this.answer3,
this.answer4,
this.iscorrectAnswer1,
this.iscorrectAnswer2,
this.iscorrectAnswer3,
this.iscorrectAnswer4,
);
#override
_QuestionsState createState() => _QuestionsState();
}
class _QuestionsState extends State<Questions> {
disableButton() {
setState(() {
widget.questionsAnswered = true;
Quiz().score += widget.score;
});
}
#override
#override
Widget build(BuildContext context) {
return Column(
children: [
SizedBox(
width: 600,
height: 600,
child: Image.asset(widget.imagePath),
),
Align(
alignment: Alignment.topCenter,
child: Padding(
padding: EdgeInsets.only(
top: 20,
),
child: Text(
widget.question,
style: TextStyle(
color: Colors.white,
fontSize: 38,
),
),
)),
Padding(
padding: EdgeInsets.only(
top: 40,
),
child: SizedBox(
width: 500,
height: 60,
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(15)),
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Color(0xFF304e60),
),
),
child: Text(
widget.answer1,
style: TextStyle(
color: Colors.white,
fontSize: 15,
),
),
onPressed: widget.questionsAnswered == false
? () {
setState(() {
if (widget.iscorrectAnswer1 == true) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Correct!'),
),
);
disableButton();
widget.score += 1;
} else {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(
content: Text('Wrong Answer!'),
));
}
});
print(widget.iscorrectAnswer1);
print(widget.score);
}
: null),
),
)),
Padding(
padding: EdgeInsets.only(
top: 10,
),
child: SizedBox(
width: 500,
height: 60,
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(15)),
child: ElevatedButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Color(0xFF565462))),
child: Text(
widget.answer2,
style: TextStyle(
color: Colors.white,
fontSize: 15,
),
),
onPressed: widget.questionsAnswered == false
? () {
setState(() {
if (widget.iscorrectAnswer2 == true) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Correct!'),
),
);
widget.score += 1;
} else {
disableButton();
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(
content: Text('Wrong Answer!'),
));
}
});
}
: null),
),
)),
Padding(
padding: EdgeInsets.only(
top: 10,
),
child: SizedBox(
width: 500,
height: 60,
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(15)),
child: ElevatedButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Color(0xFF84693b))),
child: Text(
widget.answer3,
style: TextStyle(
color: Colors.white,
fontSize: 15,
),
),
onPressed: widget.questionsAnswered == false
? () {
setState(() {
if (widget.iscorrectAnswer3 == true) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Correct!'),
),
);
widget.score += 1;
} else {
disableButton();
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(
content: Text('Wrong Answer!'),
));
}
});
}
: null),
),
),
)
],
);
}
}
Main widget where I call this custom widget
class Quiz extends StatefulWidget {
Quiz({Key? key}) : super(key: key);
int score = 0;
#override
_QuizState createState() => _QuizState();
}
class _QuizState extends State<Quiz> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('CyberQuiz'),
),
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
Questions(
'images/malware_quiz.jpeg',
'1. What is a malware?',
'Designed to damage computers, servers or any other devices',
"Used to get user's credentials",
"It's used to destroy networks",
'',
true,
false,
false,
false,
),
],
)));
}
}
As you suggest in your question, you could create a global variable and increment/decrease/reset that.
Basic example code:
import 'package:flutter/material.dart';
class Score {
static int score = 0;
}
class ScoreCounter extends StatefulWidget {
const ScoreCounter({Key? key}) : super(key: key);
#override
State<ScoreCounter> createState() => _ScoreCounterState();
}
class _ScoreCounterState extends State<ScoreCounter> {
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: ElevatedButton(
onPressed: () {
setState(() {
Score.score++;
});
},
child: Text('increase score'),
),
),
Expanded(child: Text(Score.score.toString()))
],
);
}
}
Another option is to use the Provider package - link here which has an example
Provider Package
I'm using PaginatedDataTable widget to show data from api. It works perfectly, so I have set rowsPerPage to 4 and if my data length is for example 7, I get in my first page 4 rows with data and in the second one I get tree others and one empty row. That's the problem, on the second page, I want get only rows that contains data and no empty row. Guess that everyone understands what I mean.
Thank you in advance.
my code =>
class DashboardScreen extends StatefulWidget {
DashboardScreen(
{this.token,
this.f_name,
this.phone,
this.l_name,
this.type_client,
this.email,
this.addresse,
this.num_client});
String token;
final String f_name;
final String l_name;
final String email;
final String addresse;
final String num_client;
final String phone;
final String type_client;
#override
_DashboardScreenState createState() => _DashboardScreenState();
}
class _DashboardScreenState extends State<DashboardScreen>
with SingleTickerProviderStateMixin {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
AnimationController _animationController;
Animation<double> _animateIcon;
bool isOpened = false;
String token;
String f_name;
String l_name;
String email;
String addresse;
String num_client;
String phone, type_client;
bool showSpinner = false;
List<Transaction> transactions = [];
int rowsPerPAge = 4;
Future<List<Transaction>> _getTransactionsData() async {
setState(() {
showSpinner = true;
});
GetToken getToken = GetToken(token: "${widget.token}");
var freshToken = await getToken.getTokenData();
try {
Map<String, String> newHeader = {'Authorization': 'Bearer $freshToken'};
GetUserData getUserData = GetUserData(
'${APIConstants.API_BASE_URL}/...', newHeader);
var userData = await getUserData.getData();
//print(userData);
var apiError = userData['error'];
var apiCode = userData['code'];
try {
if (apiError == false && apiCode == 200) {
final items = userData['data'].cast<Map<String, dynamic>>();
List<Transaction> listOfTransactions = items.map<Transaction> ((json) {
return Transaction.fromJson(json);
}).toList();
setState(() {
showSpinner = false;
});
//print("succes: $userData");
return listOfTransactions;
}
else if(apiCode == 401){
setState(() {
_scaffoldKey.currentState.showSnackBar(new SnackBar(
backgroundColor: MyColor.hintColor,
elevation: 10,
content: new Text(SnackBarText.TIME_OUT, style: GoogleFonts.roboto(color: Colors.white, fontSize: 20)),
));
showSpinner = false;
return MaterialPageRoute(builder: (context) => LoginScreen());
});
}
else {
//print("erreur: $userData");
setState(() {
_scaffoldKey.currentState.showSnackBar(new SnackBar(
backgroundColor: MyColor.hintColor,
elevation: 10,
content: new Text(SnackBarText.SERVER_ERROR,
style: GoogleFonts.roboto(color: Colors.white, fontSize: 20)),
));
return showSpinner = false;
});
}
} catch (e) {
print(e);
}
} catch (e) {
print(e);
print('1');
}
}
#override
void initState() {
super.initState();
token = widget.token;
f_name = widget.f_name;
l_name = widget.l_name;
email = widget.email;
addresse = widget.addresse;
num_client = widget.num_client;
phone = widget.phone;
type_client = widget.type_client;
_getTransactionsData().then((transactionsFromServer) {
transactions = transactionsFromServer;
//print(transactions);
});
_animationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 500))
..addListener(() {
setState(() {});
});
_animateIcon =
Tween<double>(begin: 1.0, end: 2.0).animate(_animationController);
}
#override
Widget build(BuildContext context) {
//FactureProvider factureProvider = Provider.of<FactureProvider>(context);
var height = MediaQuery.of(context).size.height;
var width = MediaQuery.of(context).size.width;
var orientation = MediaQuery.of(context).orientation;
bool portrait = orientation == Orientation.portrait;
return WillPopScope(
child: Scaffold(
key: _scaffoldKey,
backgroundColor: MyColor.myBackgroundColor,
appBar: AppBar(
automaticallyImplyLeading: false,
centerTitle: false,
backgroundColor: Colors.white,
leading: new IconButton(
padding: EdgeInsets.all(0.0),
icon: new Icon(
Icons.apps,
color: MyColor.menuColor,
),
onPressed: () => _scaffoldKey.currentState.openDrawer()),
title: FittedBox(
fit: BoxFit.fitWidth,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
//SizedBox(width: 30,),
//ImageIcon(AssetImage('images/notification.png'), color: Colors.black,),
Text(
Texts.DASHBOARD,
style: GoogleFonts.roboto(
color: MyColor.hintColor,
fontWeight: FontWeight.bold,
fontSize: 20),
textAlign: TextAlign.left,
),
SizedBox(
width: portrait ? width/4.0 : width/1.5,
),
GestureDetector(
onTap: _goToProfilScreen,
child: ImageIcon(
AssetImage('images/noun_avatar_1.png'),
color: Colors.black,
)),
],
),
),
),
drawer: buildDrawer,
body: Loader(
color: Colors.white.withOpacity(0.3),
loadIng: showSpinner,
child: ListView(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
Row(
children: <Widget>[
Conditioned(
cases:[
Case( (transactions?.length == 0 || transactions?.length == null), builder: () => Padding(
padding: const EdgeInsets.only(left: 10.0, top: 20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(
children: <Widget>[
Text(Texts.HISTO_TRANSAC, style: GoogleFonts.roboto(fontSize: 14, fontWeight: FontWeight.w700), textAlign: TextAlign.start,)
],
),
Padding(
padding: const EdgeInsets.all(20.0),
child: Center(child: Text(Texts.NO_TRANSAC, style: GoogleFonts.roboto(color: MyColor.hintColor, fontSize: 15),),),
),
],
),
)),
],
defaultBuilder: () => Padding(
padding: const EdgeInsets.only(left: 1.0, right: 1.0),
child: FittedBox(
fit: BoxFit.fitWidth,
child: Container(
width: MediaQuery.of(context).size.width/1.005,
child: PaginatedDataTable(
header: Text(Texts.HISTO_TRANSAC, style: GoogleFonts.roboto(fontSize: 14, fontWeight: FontWeight.w700), textAlign: TextAlign.start,),
rowsPerPage: transactions.length <= rowsPerPAge ? transactions.length : rowsPerPAge,
horizontalMargin: 3.7,
columnSpacing: 1.8,
headingRowHeight: 15,
dataRowHeight: 30,
columns: [
DataColumn(label: Text(Texts.dATE, style: GoogleFonts.roboto(fontSize: 8, fontWeight: FontWeight.w900))),
DataColumn(label: Text(Texts.mONTANT_PAYE, style: GoogleFonts.roboto(fontSize: 8, fontWeight: FontWeight.w900))),
DataColumn(label: Text(Texts.SERVICE_TEXT, style: GoogleFonts.roboto(fontSize: 8, fontWeight: FontWeight.w900))),
DataColumn(label: Text(Texts.mODE_PAIEMENT, style: GoogleFonts.roboto(fontSize: 8, fontWeight: FontWeight.w900))),
DataColumn(label: Text(Texts.DETAILS, style: GoogleFonts.roboto(fontSize: 8, fontWeight: FontWeight.w900))),
],
source: DTS(transactions, context, abonnementsById)
),
),
),
),
),
],
),
),
)
],
),
),
bottomNavigationBar: GestureDetector(
onTap: () => showMaterialModalBottomSheet(
context: context,
useRootNavigator: true,
bounce: true,
//secondAnimation: AnimationController.unbounded(vsync: this, duration: Duration(seconds: 30)),
enableDrag: true,
backgroundColor: Colors.transparent,
builder: (context, scrollController) => buildWrap(context),
),
child: Container(
color: MyColor.bottonNavColor,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text(
Texts.NEW,
style: GoogleFonts.roboto(
color: MyColor.hintColor,
fontSize: 20,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 50,
width: 50,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: FloatingActionButton(
clipBehavior: Clip.hardEdge,
autofocus: true,
mini: true,
backgroundColor: MyColor.hintColor,
onPressed: () => showMaterialModalBottomSheet(
context: context,
useRootNavigator: true,
bounce: true,
//secondAnimation: AnimationController.unbounded(vsync: this, duration: Duration(seconds: 30)),
enableDrag: true,
backgroundColor: Colors.transparent,
builder: (context, scrollController) =>
buildWrap(context),
),
child: AnimatedIcon(
icon: AnimatedIcons.event_add,
size: 30,
progress: _animateIcon,
),
),
),
)
],
),
),
),
),
onWillPop: _onWillPop,
);
}
}
You can add a checker on PaginatedDataTable's onPageChanged to check if the number of items to be displayed on the table is less than the default number of rows.
PaginatedDataTable(
rowsPerPage: _rowsPerPage,
source: RowSource(),
onPageChanged: (int? n) {
/// value of n is the number of rows displayed so far
setState(() {
if (n != null) {
debugPrint(
'onRowsPerPageChanged $_rowsPerPage ${RowSource()._rowCount - n}');
/// Update rowsPerPage if the remaining count is less than the default rowsPerPage
if (RowSource()._rowCount - n < _rowsPerPage)
_rowsPerPage = RowSource()._rowCount - n;
/// else, restore default rowsPerPage value
else _rowsPerPage = PaginatedDataTable.defaultRowsPerPage;
} else
_rowsPerPage = 0;
});
},
columns: <DataColumn>[],
)
Here's the complete sample.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
/// Set default number of rows to be displayed per page
var _rowsPerPage = PaginatedDataTable.defaultRowsPerPage;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: SafeArea(
child: PaginatedDataTable(
rowsPerPage: _rowsPerPage,
source: RowSource(),
onPageChanged: (int? n) {
/// value of n is the number of rows displayed so far
setState(() {
if (n != null) {
debugPrint(
'onRowsPerPageChanged $_rowsPerPage ${RowSource()._rowCount - n}');
/// Update rowsPerPage if the remaining count is less than the default rowsPerPage
if (RowSource()._rowCount - n < _rowsPerPage)
_rowsPerPage = RowSource()._rowCount - n;
/// else, restore default rowsPerPage value
else _rowsPerPage = PaginatedDataTable.defaultRowsPerPage;
} else
_rowsPerPage = 0;
});
},
columns: [
DataColumn(
label: Text(
'Foo',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Bar',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
],
),
),
);
}
}
class RowSource extends DataTableSource {
final _rowCount = 26;
#override
DataRow? getRow(int index) {
if (index < _rowCount) {
return DataRow(cells: <DataCell>[
DataCell(Text('Foo $index')),
DataCell(Text('Bar $index'))
]);
} else
return null;
}
#override
bool get isRowCountApproximate => true;
#override
int get rowCount => _rowCount;
#override
int get selectedRowCount => 0;
}