This commit is contained in:
Hamza-Ayed
2023-08-15 14:12:20 +03:00
parent 84ab0e86dd
commit 65ade9c874
19 changed files with 619 additions and 166 deletions

View File

@@ -17,6 +17,9 @@
</activity>
<meta-data android:name="com.google.android.geo.API_KEY" android:value="AIzaSyCyfwRXTwSTLOFQSQgN5p7QZgGJVZnEKq0" />
<meta-data android:name="com.google.firebase.messaging.default_notification_channel_id" android:value="@string/default_notification_channel_id" />
<meta-data android:name="com.android.application.versionCode" android:value="1" />
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data android:name="flutterEmbedding" android:value="2" />

View File

@@ -0,0 +1,4 @@
<resources>
<string name="app_name">My App</string>
<string name="default_notification_channel_id">ride_channel</string>
</resources>

BIN
assets/images/education.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
assets/images/gender.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -5,6 +5,7 @@ class BoxName {
static const String agreeTerms = "agreeTerms";
static const String pasengerID = "pasengerID";
static const String phone = "phone";
static const String name = "name";
static const String email = "email";
static const String tokens = "tokens";
static const String tokenFCM = "tokenFCM";

View File

@@ -19,12 +19,11 @@ class AppLink {
static const String updateCarsLocationByPassenger = "$location/update.php";
//==================Blog=============
static const String blog =
'https://portfolio.mobile-app.store/hamzaayed/blogs';
static const String getBlog = "$blog/get.php";
static const String addBlog = "$blog/add.php";
static const String deleteBlog = "$blog/delete.php";
static const String updateBlog = "$blog/update.php";
static const String profile = 'https://ride.mobile-app.store/ride/profile';
static const String getprofile = "$profile/get.php";
static const String addprofile = "$profile/add.php";
static const String deleteprofile = "$profile/delete.php";
static const String updateprofile = "$profile/update.php";
//===================Auth============
static const String auth = 'https://ride.mobile-app.store/auth';

View File

@@ -51,18 +51,22 @@ class LoginController extends GetxController {
// print(jsonDecoeded);
if (jsonDecoeded.isNotEmpty) {
if (jsonDecoeded['status'] == 'success') {
box.write(BoxName.pasengerID, jsonDecoeded['data'][0]['id']);
box.write(BoxName.email, jsonDecoeded['data'][0]['email']);
box.write(BoxName.phone, jsonDecoeded['data'][0]['phone']);
Get.offAll(() => MapPage());
isloading = false;
update();
print(box.read(BoxName.pasengerID).toString());
await CRUD().post(link: AppLink.addTokens, payload: {
'token': box.read(BoxName.tokenFCM.toString()),
'passengerID': box.read(BoxName.pasengerID).toString()
}).then((value) => print('cccc'));
if (jsonDecoeded['data'][0]['verified'] == '1') {
box.write(BoxName.pasengerID, jsonDecoeded['data'][0]['id']);
box.write(BoxName.email, jsonDecoeded['data'][0]['email']);
box.write(BoxName.phone, jsonDecoeded['data'][0]['phone']);
Get.offAll(() => MapPage());
isloading = false;
update();
print(box.read(BoxName.pasengerID).toString());
await CRUD().post(link: AppLink.addTokens, payload: {
'token': box.read(BoxName.tokenFCM.toString()),
'passengerID': box.read(BoxName.pasengerID).toString()
}).then((value) => print('cccc'));
} else {
Get.snackbar('You must Verify email !.'.tr, '',
backgroundColor: Colors.yellow[300]);
}
} else if (jsonDecoeded['status'] == 'Failure') {
Get.snackbar(jsonDecoeded['status'], jsonDecoeded['data'],
backgroundColor: Colors.redAccent);

View File

@@ -21,14 +21,14 @@ class CRUD {
'Basic ${base64Encode(utf8.encode(AppCredintials.basicAuthCredentials))}',
},
);
// print("--------------" + response.body.toString());
// print("--------------" + response.request.toString());
if (response.statusCode == 200) {
var jsonData = jsonDecode(response.body);
if (jsonData['status'] == 'success') {
print(jsonData['data']);
print(jsonData);
return response.body;
}
return response.body;
return jsonData['status'];
}
}

View File

@@ -1,5 +1,5 @@
import 'dart:convert';
import 'dart:math' show sqrt, pi, cos, sin;
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
@@ -10,6 +10,8 @@ import 'package:ride/constant/links.dart';
import 'package:ride/controller/functions/crud.dart';
import 'package:ride/views/home/map_widget.dart/buttom_sheet_map_show.dart';
import '../../models/model/locations.dart';
class MapController extends GetxController {
bool isloading = true;
TextEditingController placeController = TextEditingController();
@@ -35,6 +37,9 @@ class MapController extends GetxController {
bool isPickerShown = false;
bool isButtomSheetShown = false;
double heightButtomSheetShown = 240;
late final LatLng southwest;
late final LatLng northeast;
List<CarLocationModel> carLocations = <CarLocationModel>[];
void changeButtomSheetShown() {
isButtomSheetShown = !isButtomSheetShown;
@@ -169,10 +174,22 @@ class MapController extends GetxController {
}
Future getCarsLocationByPassenger() async {
LatLngBounds bounds =
calculateBounds(mylocation.latitude, mylocation.longitude, 10);
print(
'Southwest: ${bounds.southwest.latitude}, ${bounds.southwest.longitude}');
print(
'Northeast: ${bounds.northeast.latitude}, ${bounds.northeast.longitude}');
var res =
await CRUD().get(link: AppLink.getCarsLocationByPassenger, payload: {});
await CRUD().get(link: AppLink.getCarsLocationByPassenger, payload: {
'southwestLat': southwest.latitude.toString(),
'southwestLon': southwest.longitude.toString(),
'northeastLat': northeast.latitude.toString(),
'northeastLon': northeast.longitude.toString(),
});
var data = jsonDecode(res);
print(data);
// print(data);
for (var i = 0; i < data.length; i++) {
carsLocationByPassenger.add(LatLng(
double.parse(data['message'][i]['latitude']),
@@ -182,6 +199,23 @@ class MapController extends GetxController {
update();
}
LatLngBounds calculateBounds(
double centerLat, double centerLng, double radius) {
double radius = 2000; // 10 km in meters
southwest = LatLng(
centerLat - (radius / 111000),
centerLng - (radius / (111000 * cos(centerLat))),
);
northeast = LatLng(
centerLat + (radius / 111000),
centerLng + (radius / (111000 * cos(centerLat))),
);
return LatLngBounds(southwest: southwest, northeast: northeast);
}
GoogleMapController? mapController;
void onMapCreated(GoogleMapController controller) {
mapController = controller;
@@ -395,7 +429,7 @@ class MapController extends GetxController {
void onInit() async {
// getPolyLine();
// getMap();
getLocation();
await getLocation();
await getCarsLocationByPassenger();
addCustomPicker();
addCustomCarIcon();

View File

@@ -0,0 +1,93 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:ride/constant/box_name.dart';
import 'package:ride/constant/links.dart';
import 'package:ride/controller/functions/crud.dart';
import 'package:ride/main.dart';
import '../../views/widgets/elevated_btn.dart';
class ProfileController extends GetxController {
bool isloading = false;
Map prfoileData = {};
TextEditingController txtController = TextEditingController();
List genders = ['Male', 'Female', 'Non-binary'];
String gender = '';
void setGender(String value) {
gender = value;
}
String? selectedDegree;
void setDegree(String? degree) {
selectedDegree = degree;
update();
}
updateColumn(Map<String, dynamic> payload) async {
isloading = true;
update();
await CRUD().post(link: AppLink.updateprofile, payload: payload);
await getProfile();
isloading = false;
update();
}
updatField(String columnName) async {
Get.defaultDialog(
title: '${'Update'.tr} $columnName',
content: Column(
children: [
SizedBox(
width: Get.width * .7,
child: TextField(
controller: txtController,
decoration: const InputDecoration(
border: OutlineInputBorder(), hintText: 'type here'),
),
),
MyElevatedButton(
title: 'Update'.tr,
onPressed: () {
updateColumn({
'id': prfoileData['id'].toString(),
columnName: txtController.text,
});
Get.back();
txtController.clear();
},
)
],
),
);
}
getProfile() async {
isloading = true;
update();
var res = await CRUD().get(link: AppLink.getprofile, payload: {
'id': box.read(BoxName.pasengerID).toString(),
});
if (res.toString() == 'failure') {
Get.snackbar('failure', 'message');
isloading = false;
update();
} else {
var jsonDecoded = jsonDecode(res);
prfoileData = jsonDecoded['data'];
isloading = false;
update();
}
}
@override
void onInit() {
getProfile();
super.onInit();
}
}

View File

@@ -39,7 +39,8 @@ void main() async {
FirebasMessagesController().getToken();
await FirebasMessagesController().getTokens();
FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler);
FirebasMessagesController().getNotificationSettings();
FirebasMessagesController().requestPermission();
runApp(const MyApp());
}
@@ -63,8 +64,8 @@ class MyApp extends StatelessWidget {
home: box.read(BoxName.lang).toString() != 'ar' &&
box.read(BoxName.lang).toString() != 'en'
? const Language()
: box.read(BoxName.email).toString() != null
? const MapPage()
: LoginPage());
: box.read(BoxName.email) == null
? LoginPage()
: const MapPage());
}
}

57
lib/models/db_sql.dart Normal file
View File

@@ -0,0 +1,57 @@
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DbSql {
static final DbSql instance = DbSql._();
static Database? _database;
DbSql._();
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}
Future<Database> _initDatabase() async {
String path = join(await getDatabasesPath(), 'my_database.db');
return await openDatabase(
path,
version: 1,
onCreate: (db, version) async {
await db.execute('''
CREATE TABLE IF NOT EXISTS car_locations(
id INTEGER PRIMARY KEY AUTOINCREMENT,
driver_id INTEGER,
latitude REAL,
longitude REAL,
created_at TEXT,
updated_at TEXT
)
''');
},
);
}
Future<List<Map<String, dynamic>>> getAllData(String table) async {
Database db = await instance.database;
return await db.query(table);
}
Future<int> insertData(Map<String, dynamic> map, String table) async {
Database db = await instance.database;
return await db.insert(table, map);
}
Future<int> updateData(Map<String, dynamic> map, String table, int id) async {
Database db = await instance.database;
return await db.update(table, map, where: 'id = ?', whereArgs: [id]);
}
Future<int> deleteData(String table, int id) async {
Database db = await instance.database;
return await db.delete(table, where: 'id = ?', whereArgs: [id]);
}
}

View File

@@ -0,0 +1,28 @@
class CarLocationModel {
int id;
int driverId;
double latitude;
double longitude;
DateTime createdAt;
DateTime updatedAt;
CarLocationModel({
required this.id,
required this.driverId,
required this.latitude,
required this.longitude,
required this.createdAt,
required this.updatedAt,
});
factory CarLocationModel.fromJson(Map<String, dynamic> json) {
return CarLocationModel(
id: json['id'],
driverId: json['driver_id'],
latitude: json['latitude'],
longitude: json['longitude'],
createdAt: DateTime.parse(json['created_at']),
updatedAt: DateTime.parse(json['updated_at']),
);
}
}

View File

@@ -20,142 +20,156 @@ class LoginPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
Get.put(LoginController());
return MyScafolld(title: 'Login', isleading: false, body: [
if (box.read(BoxName.agreeTerms) != 'agreed')
agreedpage()
else
SingleChildScrollView(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(25),
child: Container(
decoration: const BoxDecoration(
boxShadow: [
BoxShadow(
offset: Offset(3, 3),
color: AppColor.accentColor,
blurRadius: 3)
],
color: AppColor.secondaryColor,
),
child: Form(
key: controller.formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: Column(
children: [
TextFormField(
keyboardType: TextInputType.emailAddress,
controller: controller.emailController,
decoration: InputDecoration(
fillColor: AppColor.accentColor,
hoverColor: AppColor.accentColor,
focusColor: AppColor.accentColor,
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(12))),
labelText: 'Email'.tr,
hintText: 'Enter your email address'.tr,
),
validator: (value) {
if (value!.isEmpty ||
(!value.contains('@') ||
!value.contains('.'))) {
return 'Please enter Your Email.'.tr;
}
return null;
},
),
const SizedBox(
height: 30,
),
TextFormField(
keyboardType: TextInputType.phone,
cursorColor: AppColor.accentColor,
controller: controller.phoneController,
decoration: InputDecoration(
focusColor: AppColor.accentColor,
fillColor: AppColor.accentColor,
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(12))),
labelText: 'Phone'.tr,
hintText: 'Enter your phone number'.tr,
),
validator: (value) {
if (value!.isEmpty || value.length != 10) {
return 'Please enter your phone number.'.tr;
}
return null;
},
),
const SizedBox(
height: 15,
),
TextFormField(
obscureText: true,
keyboardType: TextInputType.emailAddress,
controller: controller.passwordController,
decoration: InputDecoration(
fillColor: AppColor.accentColor,
hoverColor: AppColor.accentColor,
focusColor: AppColor.accentColor,
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(12))),
labelText: 'Password'.tr,
hintText: 'Enter your Password'.tr,
),
validator: (value) {
if (value!.isEmpty || (value.length > 6)) {
return 'Please enter Your Password.'.tr;
}
return null;
},
),
GetBuilder<LoginController>(
builder: (controller) => controller.isloading
? const MyCircularProgressIndicator()
: MyElevatedButton(
onPressed: () {
if (controller.formKey.currentState!
.validate()) {
controller.login();
return GetBuilder<LoginController>(
builder: (controller) =>
MyScafolld(title: 'Login', isleading: false, body: [
if (box.read(BoxName.agreeTerms) != 'agreed')
agreedpage()
else
SingleChildScrollView(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(25),
child: Container(
decoration: const BoxDecoration(
boxShadow: [
BoxShadow(
offset: Offset(3, 3),
color: AppColor.accentColor,
blurRadius: 3)
],
color: AppColor.secondaryColor,
),
child: Form(
key: controller.formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: Column(
children: [
TextFormField(
keyboardType:
TextInputType.emailAddress,
controller: controller.emailController,
decoration: InputDecoration(
fillColor: AppColor.accentColor,
hoverColor: AppColor.accentColor,
focusColor: AppColor.accentColor,
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(12))),
labelText: 'Email'.tr,
hintText:
'Enter your email address'.tr,
),
validator: (value) {
if (value!.isEmpty ||
(!value.contains('@') ||
!value.contains('.'))) {
return 'Please enter Your Email.'
.tr;
}
return null;
},
title: 'Submit',
),
)
],
),
),
const SizedBox(
height: 30,
),
TextFormField(
keyboardType: TextInputType.phone,
cursorColor: AppColor.accentColor,
controller: controller.phoneController,
decoration: InputDecoration(
focusColor: AppColor.accentColor,
fillColor: AppColor.accentColor,
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(12))),
labelText: 'Phone'.tr,
hintText:
'Enter your phone number'.tr,
),
validator: (value) {
if (value!.isEmpty ||
value.length != 10) {
return 'Please enter your phone number.'
.tr;
}
return null;
},
),
const SizedBox(
height: 15,
),
TextFormField(
obscureText: true,
keyboardType:
TextInputType.emailAddress,
controller:
controller.passwordController,
decoration: InputDecoration(
fillColor: AppColor.accentColor,
hoverColor: AppColor.accentColor,
focusColor: AppColor.accentColor,
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(12))),
labelText: 'Password'.tr,
hintText: 'Enter your Password'.tr,
),
validator: (value) {
if (value!.isEmpty ||
(value.length > 6)) {
return 'Please enter Your Password.'
.tr;
}
return null;
},
),
GetBuilder<LoginController>(
builder: (controller) => controller
.isloading
? const MyCircularProgressIndicator()
: MyElevatedButton(
onPressed: () {
if (controller
.formKey.currentState!
.validate()) {
controller.login();
}
},
title: 'Submit',
),
)
],
),
),
),
),
)),
Text(
'if you dont have account'.tr,
style: AppStyle.subtitle,
),
),
)),
Text(
'if you dont have account'.tr,
style: AppStyle.subtitle,
),
AnimatedTextKit(
onTap: () => Get.to(() => const RegisterPage()),
animatedTexts: [
TypewriterAnimatedText(
'Register',
textStyle: AppStyle.headtitle2,
speed: const Duration(milliseconds: 200),
AnimatedTextKit(
onTap: () => Get.to(() => const RegisterPage()),
animatedTexts: [
TypewriterAnimatedText(
'Register',
textStyle: AppStyle.headtitle2,
speed: const Duration(milliseconds: 200),
),
],
totalRepeatCount: 4,
pause: const Duration(milliseconds: 200),
displayFullTextOnTap: true,
stopPauseOnTap: true,
)
],
),
],
totalRepeatCount: 4,
pause: const Duration(milliseconds: 200),
displayFullTextOnTap: true,
stopPauseOnTap: true,
)
],
),
)
]);
)
]));
}
Padding agreedpage() {

View File

@@ -50,6 +50,7 @@ class MapPage extends StatelessWidget {
},
);
},
onTap: (argument) {
controller.hidePlaces();
@@ -99,11 +100,34 @@ class MapPage extends StatelessWidget {
color: AppColor.primaryColor,
width: 5,
),
// Polyline(
// zIndex: 2,
// consumeTapEvents: true,
// geodesic: true,
// endCap: Cap.buttCap,
// startCap: Cap.buttCap,
// visible: true,
// polylineId: PolylineId('g'),
// points: [
// LatLng(controller.southwest.latitude,
// controller.southwest.longitude),
// LatLng(controller.northeast.latitude,
// controller.northeast.longitude)
// ],
// color: AppColor.primaryColor,
// width: 5,
// ),
},
// circles: {
// Circle(
// circleId: const CircleId('kk'),
// center: controller.mylocation,
// radius: 2000)
// },
mapType: MapType.normal,
myLocationButtonEnabled: true,
indoorViewEnabled: true,
trafficEnabled: true,
trafficEnabled: false,
buildingsEnabled: true,
mapToolbarEnabled: true,
onCameraMove: (position) {

View File

@@ -5,7 +5,6 @@ import '../../../constant/colors.dart';
import '../../../constant/style.dart';
import '../../../controller/home/map_page_controller.dart';
import '../../widgets/elevated_btn.dart';
import 'buttom_sheet_map_show.dart';
import 'form_search_places.dart';
class PickerAnimtionContainer extends StatelessWidget {

View File

@@ -1,19 +1,211 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:ride/constant/colors.dart';
import 'package:ride/constant/style.dart';
import 'package:ride/controller/profile/profile_controller.dart';
import 'package:ride/views/widgets/elevated_btn.dart';
import 'package:ride/views/widgets/my_scafold.dart';
import 'package:ride/views/widgets/mycircular.dart';
class PassengerProfilePage extends StatelessWidget {
const PassengerProfilePage({super.key});
@override
Widget build(BuildContext context) {
return const MyScafolld(
Get.put(ProfileController());
return MyScafolld(
isleading: true,
title: 'My Profile',
body: [
Center(
child: Text('ddd'),
),
GetBuilder<ProfileController>(
builder: (controller) => controller.isloading
? const MyCircularProgressIndicator()
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Edit Profile'.tr,
style: AppStyle.headtitle2,
),
ListTile(
title: Text(
'Name'.tr,
style: AppStyle.title,
),
leading: const Icon(
Icons.person_pin_rounded,
size: 35,
),
trailing: const Icon(Icons.arrow_forward_ios),
subtitle: Text(
'${controller.prfoileData['first_name']} ${controller.prfoileData['last_name']}'),
onTap: () {
controller.updatField('first_name');
},
),
ListTile(
title: Text(
'Gender'.tr,
style: AppStyle.title,
),
leading: Image.asset(
'assets/images/gender.png',
width: 35,
),
trailing: const Icon(Icons.arrow_forward_ios),
subtitle: Text(
controller.prfoileData['gender'].toString()),
onTap: () {
Get.defaultDialog(
title: 'Update Gender'.tr,
content: Column(
children: [
GenderPicker(),
MyElevatedButton(
title: 'Update'.tr,
onPressed: () {
controller.updateColumn({
'id': controller.prfoileData['id']
.toString(),
'gender': controller.gender,
});
Get.back();
},
)
],
));
// controller.updatField('gender');
},
),
ListTile(
title: Text(
'Education'.tr,
style: AppStyle.title,
),
leading: Image.asset(
'assets/images/education.png',
width: 35,
),
trailing: const Icon(Icons.arrow_forward_ios),
subtitle: Text(
controller.prfoileData['education'].toString()),
onTap: () {
Get.defaultDialog(
title: 'Update Education'.tr,
content: Column(
children: [
EducationDegreePicker(),
MyElevatedButton(
title: 'Update Education'.tr,
onPressed: () {
controller.updateColumn({
'id': controller.prfoileData['id']
.toString(),
'education':
controller.selectedDegree,
});
Get.back();
},
)
],
));
},
),
ListTile(
title: Text(
'Employment Type'.tr,
style: AppStyle.title,
),
leading: Image.asset(
'assets/images/employmentType.png',
width: 35,
),
trailing: const Icon(Icons.arrow_forward_ios),
subtitle: Text(controller
.prfoileData['employmentType']
.toString()),
onTap: () {
controller.updatField('employmentType');
},
),
ListTile(
title: Text(
'Marital Status'.tr,
style: AppStyle.title,
),
leading: Image.asset(
'assets/images/maritalStatus.png',
width: 35,
),
trailing: const Icon(Icons.arrow_forward_ios),
subtitle: Text(controller
.prfoileData['maritalStatus']
.toString()),
onTap: () {
controller.updatField('maritalStatus');
},
),
],
),
),
))
],
);
}
}
class GenderPicker extends StatelessWidget {
final ProfileController controller = Get.put(ProfileController());
final List<String> genderOptions = ['Male', 'Female', 'Other'];
GenderPicker({super.key});
@override
Widget build(BuildContext context) {
return CupertinoPicker(
itemExtent: 32.0,
onSelectedItemChanged: (int index) {
controller.setGender(genderOptions[index]);
},
children: genderOptions.map((String value) {
return Text(value);
}).toList(),
);
}
}
class EducationDegreePicker extends StatelessWidget {
final ProfileController controller = Get.put(ProfileController());
final List<String> degreeOptions = [
'High School Diploma',
'Associate Degree',
'Bachelor\'s Degree',
'Master\'s Degree',
'Doctoral Degree',
];
EducationDegreePicker({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return CupertinoPicker(
// backgroundColor: AppColor.accentColor,
looping: true,
squeeze: 2, diameterRatio: 2,
itemExtent: 32.0,
onSelectedItemChanged: (int index) {
controller.setDegree(degreeOptions[index]);
},
children: degreeOptions.map((String value) {
return Text(value);
}).toList(),
);
}
}