ft/adds person_detail_page
This commit is contained in:
parent
e2d3720728
commit
6d48638ef1
@ -1,11 +1,15 @@
|
||||
import 'package:app/pages/home_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart'
|
||||
show GlobalMaterialLocalizations;
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
runApp(
|
||||
MaterialApp(
|
||||
localizationsDelegates: const [GlobalMaterialLocalizations.delegate],
|
||||
supportedLocales: const [Locale('en'), Locale('de')],
|
||||
theme: ThemeData().copyWith(
|
||||
colorScheme: const ColorScheme(
|
||||
brightness: Brightness.dark,
|
||||
|
@ -4,6 +4,7 @@ class AppException implements Exception {
|
||||
|
||||
AppException([this._message, this._prefix]);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "$_prefix$_message";
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import 'package:app/pb/rpc_get_person.pb.dart';
|
||||
import 'package:app/pb/rpc_list_persons.pb.dart';
|
||||
import 'package:app/pb/rpc_login.pb.dart';
|
||||
import 'package:app/pb/rpc_refresh_token.pb.dart';
|
||||
import 'package:app/pb/rpc_update_person.pb.dart';
|
||||
import 'package:app/pb/service_df.pbgrpc.dart';
|
||||
import 'package:fixnum/fixnum.dart';
|
||||
import 'package:grpc/grpc.dart';
|
||||
@ -114,6 +115,10 @@ class BackendService {
|
||||
return true;
|
||||
}
|
||||
|
||||
static Future<Int64?> get accountId async {
|
||||
return (await Session.session).accountId;
|
||||
}
|
||||
|
||||
static Future<bool> createAccount(
|
||||
{required String email, required String password}) async {
|
||||
try {
|
||||
@ -207,14 +212,15 @@ class BackendService {
|
||||
}
|
||||
}
|
||||
|
||||
Future<Person> createPerson(
|
||||
{required String firstname,
|
||||
required String lastname,
|
||||
required String street,
|
||||
required String zip,
|
||||
required String city,
|
||||
required String country,
|
||||
required DateTime birthday}) async {
|
||||
Future<Person> updatePerson(
|
||||
{required Int64 id,
|
||||
String? firstname,
|
||||
String? lastname,
|
||||
String? street,
|
||||
String? zip,
|
||||
String? city,
|
||||
String? country,
|
||||
Timestamp? birthday}) async {
|
||||
Session session = await Session.session;
|
||||
if (session.accessTokenExpiresAt == null) {
|
||||
throw UnauthorizedException('Keine Siztung gefunden');
|
||||
@ -226,16 +232,74 @@ class BackendService {
|
||||
}
|
||||
}
|
||||
try {
|
||||
final CreatePersonResponse response = await _client.createPerson(
|
||||
CreatePersonRequest(
|
||||
accountId: session.accountId,
|
||||
lastname: lastname,
|
||||
firstname: firstname,
|
||||
street: street,
|
||||
zip: zip,
|
||||
country: country,
|
||||
birthday: Timestamp.fromDateTime(birthday),
|
||||
),
|
||||
final UpdatePersonRequest req = UpdatePersonRequest(
|
||||
id: id,
|
||||
);
|
||||
|
||||
if (lastname != null) {
|
||||
req.lastname = lastname;
|
||||
}
|
||||
if (firstname != null) {
|
||||
req.firstname = firstname;
|
||||
}
|
||||
if (street != null) {
|
||||
req.street = street;
|
||||
}
|
||||
if (city != null) {
|
||||
req.city = city;
|
||||
}
|
||||
if (zip != null) {
|
||||
req.zip = zip;
|
||||
}
|
||||
if (country != null) {
|
||||
req.country = country;
|
||||
}
|
||||
if (birthday != null) {
|
||||
req.birthday = birthday;
|
||||
}
|
||||
final UpdatePersonResponse response = await _client.updatePerson(req,
|
||||
options: CallOptions(
|
||||
metadata: {'Authorization': 'Bearer ${session.accessToken}'}));
|
||||
return response.person;
|
||||
} on SocketException {
|
||||
throw FetchDataException('Keine Internet Verbindung');
|
||||
} on GrpcError catch (err) {
|
||||
throw FetchDataException(err.message);
|
||||
} catch (err) {
|
||||
throw InternalException(err.toString());
|
||||
}
|
||||
}
|
||||
|
||||
Future<Person> createPerson(
|
||||
{required String firstname,
|
||||
required String lastname,
|
||||
required String street,
|
||||
required String zip,
|
||||
required String city,
|
||||
required String country,
|
||||
required Timestamp birthday}) async {
|
||||
Session session = await Session.session;
|
||||
if (session.accessTokenExpiresAt == null) {
|
||||
throw UnauthorizedException('Keine Siztung gefunden');
|
||||
}
|
||||
if (session.accessTokenExpiresAt!.toDateTime().isBefore(DateTime.now())) {
|
||||
session = await refreshToken(session);
|
||||
if (session.accessTokenExpiresAt == null) {
|
||||
throw UnauthorizedException('Sitzung ist abgelaufen');
|
||||
}
|
||||
}
|
||||
try {
|
||||
final CreatePersonRequest req = CreatePersonRequest(
|
||||
accountId: session.accountId,
|
||||
lastname: lastname,
|
||||
firstname: firstname,
|
||||
street: street,
|
||||
city: city,
|
||||
zip: zip,
|
||||
country: country,
|
||||
birthday: birthday,
|
||||
);
|
||||
final CreatePersonResponse response = await _client.createPerson(req,
|
||||
options: CallOptions(
|
||||
metadata: {'Authorization': 'Bearer ${session.accessToken}'}));
|
||||
return response.person;
|
||||
|
@ -7,11 +7,12 @@ class AccountViewModel extends BaseViewModel {
|
||||
AccountViewModel() {
|
||||
_init();
|
||||
}
|
||||
ApiResponse _apiResponse = ApiResponse.initial('Keine Daten');
|
||||
final ApiResponse _apiResponse = ApiResponse.initial('Keine Daten');
|
||||
|
||||
final BackendService _service = BackendService();
|
||||
Account? _account;
|
||||
|
||||
@override
|
||||
ApiResponse get response {
|
||||
return _apiResponse;
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
import 'package:app/model/apis/api_response.dart';
|
||||
import 'package:app/model/services/backend_service.dart';
|
||||
import 'package:app/pb/google/protobuf/timestamp.pb.dart';
|
||||
import 'package:app/pb/person.pb.dart';
|
||||
import 'package:app/util/colors.dart';
|
||||
import 'package:fixnum/fixnum.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class PersonsViewModel with ChangeNotifier {
|
||||
@ -35,8 +38,9 @@ class PersonsViewModel with ChangeNotifier {
|
||||
required String zip,
|
||||
required String city,
|
||||
required String country,
|
||||
required DateTime birthday}) async {
|
||||
required Timestamp birthday}) async {
|
||||
Person person = Person();
|
||||
final messenger = ScaffoldMessenger.of(context);
|
||||
_apiResponse = ApiResponse.loading('Erstelle Person');
|
||||
try {
|
||||
person = await _service.createPerson(
|
||||
@ -47,8 +51,102 @@ class PersonsViewModel with ChangeNotifier {
|
||||
city: city,
|
||||
country: country,
|
||||
birthday: birthday);
|
||||
messenger.showSnackBar(SnackBar(
|
||||
backgroundColor: CustomColors.success,
|
||||
content: const Text(
|
||||
'Gepeichert',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
));
|
||||
_apiResponse = ApiResponse.completed(person);
|
||||
} catch (err) {
|
||||
messenger.showSnackBar(SnackBar(
|
||||
backgroundColor: CustomColors.error,
|
||||
content: const Text(
|
||||
'Fehler beim Speichern',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
action: SnackBarAction(
|
||||
label: 'Details',
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
backgroundColor: Colors.black,
|
||||
icon: Icon(
|
||||
Icons.error,
|
||||
color: CustomColors.error,
|
||||
),
|
||||
content: Text(
|
||||
err.toString(),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
));
|
||||
},
|
||||
),
|
||||
));
|
||||
_apiResponse = ApiResponse.error(err.toString());
|
||||
}
|
||||
notifyListeners();
|
||||
return person;
|
||||
}
|
||||
|
||||
Future<Person> updatePerson(BuildContext context,
|
||||
{required Int64 id,
|
||||
String? firstname,
|
||||
String? lastname,
|
||||
String? street,
|
||||
String? zip,
|
||||
String? city,
|
||||
String? country,
|
||||
Timestamp? birthday}) async {
|
||||
Person person = Person();
|
||||
final messenger = ScaffoldMessenger.of(context);
|
||||
_apiResponse = ApiResponse.loading('Erstelle Person');
|
||||
try {
|
||||
person = await _service.updatePerson(
|
||||
id: id,
|
||||
firstname: firstname,
|
||||
lastname: lastname,
|
||||
street: street,
|
||||
zip: zip,
|
||||
city: city,
|
||||
country: country,
|
||||
birthday: birthday);
|
||||
messenger.showSnackBar(SnackBar(
|
||||
backgroundColor: CustomColors.success,
|
||||
content: const Text(
|
||||
'Gepeichert',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
));
|
||||
_apiResponse = ApiResponse.completed(person);
|
||||
} catch (err) {
|
||||
messenger.showSnackBar(SnackBar(
|
||||
backgroundColor: CustomColors.error,
|
||||
content: const Text(
|
||||
'Fehler beim Speichern',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
action: SnackBarAction(
|
||||
label: 'Details',
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
backgroundColor: Colors.black,
|
||||
icon: Icon(
|
||||
Icons.error,
|
||||
color: CustomColors.error,
|
||||
),
|
||||
content: Text(
|
||||
err.toString(),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
));
|
||||
},
|
||||
),
|
||||
));
|
||||
_apiResponse = ApiResponse.error(err.toString());
|
||||
}
|
||||
notifyListeners();
|
||||
|
339
frontend/app/lib/pages/person_details_page.dart
Normal file
339
frontend/app/lib/pages/person_details_page.dart
Normal file
@ -0,0 +1,339 @@
|
||||
import 'package:app/model/view_model/persons_vm.dart';
|
||||
import 'package:app/pb/google/protobuf/timestamp.pb.dart';
|
||||
import 'package:app/pb/person.pb.dart';
|
||||
import 'package:app/util/validation.dart';
|
||||
import 'package:app/widgets/background.dart';
|
||||
import 'package:app/widgets/bottom_navigation.dart';
|
||||
import 'package:app/widgets/bottom_navigation_item.dart';
|
||||
import 'package:fixnum/fixnum.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
Future<Person> showPerson(BuildContext context, {Person? person}) async {
|
||||
PersonsViewModel vm = PersonsViewModel();
|
||||
|
||||
final formKey = GlobalKey<FormState>();
|
||||
final firstnameController = TextEditingController();
|
||||
final lastnameController = TextEditingController();
|
||||
final cityController = TextEditingController();
|
||||
final zipController = TextEditingController();
|
||||
final streetController = TextEditingController();
|
||||
final countryController = TextEditingController();
|
||||
final birthdayController = TextEditingController();
|
||||
|
||||
Future<void> _init() async {
|
||||
if (person == null) {
|
||||
person ??= Person();
|
||||
// person ??= Person(accountId: await BackendService.accountId);
|
||||
} else {
|
||||
firstnameController.text = person!.firstname;
|
||||
lastnameController.text = person!.lastname;
|
||||
cityController.text = person!.city;
|
||||
zipController.text = person!.zip;
|
||||
streetController.text = person!.street;
|
||||
countryController.text = person!.country;
|
||||
birthdayController.text =
|
||||
DateFormat('dd.MM.yyyy').format(person!.birthday.toDateTime());
|
||||
}
|
||||
}
|
||||
|
||||
await _init();
|
||||
|
||||
void _updateData() {
|
||||
person!.firstname = firstnameController.text;
|
||||
person!.lastname = lastnameController.text;
|
||||
person!.city = cityController.text;
|
||||
person!.street = streetController.text;
|
||||
person!.zip = zipController.text;
|
||||
person!.country = countryController.text;
|
||||
}
|
||||
|
||||
Future<void> createPerson(BuildContext context) async {
|
||||
final navigator = Navigator.of(context);
|
||||
_updateData();
|
||||
person!.id = Int64(0);
|
||||
person = await vm.createPerson(context,
|
||||
firstname: person!.firstname,
|
||||
lastname: person!.lastname,
|
||||
street: person!.street,
|
||||
zip: person!.zip,
|
||||
city: person!.city,
|
||||
country: person!.country,
|
||||
birthday: person!.birthday);
|
||||
|
||||
if (person!.id != 0) {
|
||||
navigator.pop(person);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updatePerson(BuildContext context) async {
|
||||
final navigator = Navigator.of(context);
|
||||
_updateData();
|
||||
final personUpdate = await vm.updatePerson(context,
|
||||
id: person!.id,
|
||||
firstname: person!.firstname != firstnameController.text
|
||||
? person!.firstname
|
||||
: null,
|
||||
lastname: person!.lastname != lastnameController.text
|
||||
? person!.lastname
|
||||
: null,
|
||||
street: person!.street != streetController.text ? person!.street : null,
|
||||
zip: person!.zip != zipController.text ? person!.zip : null,
|
||||
city: person!.city != cityController.text ? person!.city : null,
|
||||
country:
|
||||
person!.country != countryController.text ? person!.country : null,
|
||||
birthday:
|
||||
DateFormat('dd.MM.yyyy').format(person!.birthday.toDateTime()) !=
|
||||
birthdayController.text
|
||||
? person!.birthday
|
||||
: null);
|
||||
|
||||
if (personUpdate != person) {
|
||||
navigator.pop(person);
|
||||
}
|
||||
}
|
||||
|
||||
// ignore: use_build_context_synchronously
|
||||
await showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (builder) {
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
bottomNavigationBar: BottomNavigation(
|
||||
hideMenu: true,
|
||||
children: [
|
||||
BottomNavigationItem(
|
||||
onPressed: () {
|
||||
Navigator.pop(context, false);
|
||||
},
|
||||
icon: Icons.arrow_back,
|
||||
color: Colors.white,
|
||||
label: 'Zurück',
|
||||
),
|
||||
BottomNavigationItem(
|
||||
onPressed: () {
|
||||
Navigator.pop(context, false);
|
||||
},
|
||||
icon: Icons.home,
|
||||
color: Colors.white,
|
||||
label: 'Home',
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 50,
|
||||
),
|
||||
Text(
|
||||
person!.id == 0 ? 'Person anlegen' : 'Person anpassen',
|
||||
style: const TextStyle(
|
||||
fontFamily: 'sans-serif',
|
||||
fontSize: 24,
|
||||
height: 1.6,
|
||||
fontWeight: FontWeight.normal,
|
||||
letterSpacing: 6),
|
||||
),
|
||||
ChangeNotifierProvider<PersonsViewModel>(
|
||||
create: (context) => PersonsViewModel(),
|
||||
child: Consumer<PersonsViewModel>(
|
||||
builder: (context, value, child) => Form(
|
||||
key: formKey,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 40,
|
||||
),
|
||||
TextFormField(
|
||||
controller: firstnameController,
|
||||
decoration: const InputDecoration(
|
||||
fillColor: Color.fromARGB(30, 255, 255, 255),
|
||||
filled: true,
|
||||
suffix: Text('Vorname'),
|
||||
hintStyle: TextStyle(
|
||||
color: Colors.white38,
|
||||
),
|
||||
hintText: 'Vorname',
|
||||
),
|
||||
keyboardType: TextInputType.name,
|
||||
validator: (value) {
|
||||
if (value == null || !value.isValidName) {
|
||||
return 'Bitte einen gültigen Vornamen eingeben';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
TextFormField(
|
||||
controller: lastnameController,
|
||||
decoration: const InputDecoration(
|
||||
fillColor: Color.fromARGB(30, 255, 255, 255),
|
||||
filled: true,
|
||||
suffix: Text('Nachname'),
|
||||
hintStyle: TextStyle(
|
||||
color: Colors.white38,
|
||||
),
|
||||
hintText: 'Nachname',
|
||||
),
|
||||
keyboardType: TextInputType.name,
|
||||
validator: (value) {
|
||||
if (value == null || !value.isValidName) {
|
||||
return 'Bitte einen gültigen Nachnamen eingeben';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
TextFormField(
|
||||
readOnly: true,
|
||||
onTap: () async {
|
||||
DateTime? pickedDate = await showDatePicker(
|
||||
context: context,
|
||||
locale: const Locale('de', 'DE'),
|
||||
initialDate: DateTime.now(),
|
||||
firstDate: DateTime(1930),
|
||||
lastDate: DateTime(DateTime.now().year + 1),
|
||||
builder: (context, child) => Theme(
|
||||
data: ThemeData.dark(),
|
||||
child: child != null ? child : Text(''),
|
||||
),
|
||||
);
|
||||
|
||||
if (pickedDate != null) {
|
||||
person!.birthday =
|
||||
Timestamp.fromDateTime(pickedDate);
|
||||
birthdayController.text =
|
||||
DateFormat('dd.MM.yyyy')
|
||||
.format(pickedDate);
|
||||
}
|
||||
},
|
||||
controller: birthdayController,
|
||||
decoration: const InputDecoration(
|
||||
fillColor: Color.fromARGB(30, 255, 255, 255),
|
||||
filled: true,
|
||||
suffix: Text('Geburtstag'),
|
||||
hintStyle: TextStyle(
|
||||
color: Colors.white38,
|
||||
),
|
||||
hintText: 'Geburtstag',
|
||||
),
|
||||
keyboardType: TextInputType.name,
|
||||
// validator: (value) {
|
||||
// if (value == null || !value.isValidName) {
|
||||
// return 'Bitte einen gültigen Nachnamen eingeben';
|
||||
// }
|
||||
// return null;
|
||||
// },
|
||||
),
|
||||
TextFormField(
|
||||
controller: streetController,
|
||||
decoration: const InputDecoration(
|
||||
fillColor: Color.fromARGB(30, 255, 255, 255),
|
||||
filled: true,
|
||||
suffix: Text('Straße'),
|
||||
hintStyle: TextStyle(
|
||||
color: Colors.white38,
|
||||
),
|
||||
hintText: 'Straße mit Hausnummer',
|
||||
),
|
||||
keyboardType: TextInputType.name,
|
||||
validator: (value) {
|
||||
if (value == null || !value.isValidName) {
|
||||
return 'Bitte eine gültige Straße mit Hausnummer eingeben';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
TextFormField(
|
||||
controller: zipController,
|
||||
decoration: const InputDecoration(
|
||||
fillColor: Color.fromARGB(30, 255, 255, 255),
|
||||
filled: true,
|
||||
suffix: Text('PLZ'),
|
||||
hintStyle: TextStyle(
|
||||
color: Colors.white38,
|
||||
),
|
||||
hintText: 'PLZ',
|
||||
),
|
||||
keyboardType: TextInputType.name,
|
||||
validator: (value) {
|
||||
if (value == null || !value.isValidName) {
|
||||
return 'Bitte eine gültige PLZ eingeben';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
TextFormField(
|
||||
controller: cityController,
|
||||
decoration: const InputDecoration(
|
||||
fillColor: Color.fromARGB(30, 255, 255, 255),
|
||||
filled: true,
|
||||
suffix: Text('Stadt'),
|
||||
hintStyle: TextStyle(
|
||||
color: Colors.white38,
|
||||
),
|
||||
hintText: 'Stadt',
|
||||
),
|
||||
keyboardType: TextInputType.name,
|
||||
validator: (value) {
|
||||
if (value == null || !value.isValidName) {
|
||||
return 'Bitte eine gültige Stadt eingeben';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
TextFormField(
|
||||
controller: countryController,
|
||||
decoration: const InputDecoration(
|
||||
fillColor: Color.fromARGB(30, 255, 255, 255),
|
||||
filled: true,
|
||||
suffix: Text('Land'),
|
||||
hintStyle: TextStyle(
|
||||
color: Colors.white38,
|
||||
),
|
||||
hintText: 'Land',
|
||||
),
|
||||
keyboardType: TextInputType.name,
|
||||
validator: (value) {
|
||||
if (value == null || !value.isValidName) {
|
||||
return 'Bitte ein gültiges Land eingeben';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
person!.id.isZero
|
||||
? await createPerson(context)
|
||||
: await updatePerson(context);
|
||||
},
|
||||
child: const Icon(Icons.update),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
useSafeArea: true,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.black);
|
||||
return person!;
|
||||
}
|
@ -2,7 +2,9 @@ import 'package:app/model/apis/api_response.dart';
|
||||
import 'package:app/model/services/backend_service.dart';
|
||||
import 'package:app/model/view_model/persons_vm.dart';
|
||||
import 'package:app/pages/home_page.dart';
|
||||
import 'package:app/pages/person_details_page.dart';
|
||||
import 'package:app/pb/person.pb.dart';
|
||||
import 'package:app/util/validation.dart';
|
||||
import 'package:app/widgets/background.dart';
|
||||
import 'package:app/widgets/bottom_navigation.dart';
|
||||
import 'package:app/widgets/bottom_navigation_item.dart';
|
||||
@ -61,13 +63,56 @@ class _PersonsPageState extends State<PersonsPage> {
|
||||
}
|
||||
|
||||
List<Widget> _personsList(List<Person> persons) {
|
||||
persons.sort((a, b) {
|
||||
final comp = a.lastname.compareTo(b.lastname);
|
||||
if (comp != 0) {
|
||||
return comp;
|
||||
}
|
||||
return a.firstname.compareTo(b.firstname);
|
||||
});
|
||||
final List<Widget> list = [];
|
||||
for (var p in persons) {
|
||||
list.add(Card(
|
||||
color: Colors.black,
|
||||
child: Text(
|
||||
'${p.firstname} ${p.lastname}',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
list.add(TextButton(
|
||||
onPressed: () async {
|
||||
final Person per = await showPerson(context, person: p);
|
||||
setState(() {
|
||||
this.persons.add(per);
|
||||
});
|
||||
},
|
||||
child: Card(
|
||||
shape:
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
color: const Color.fromARGB(100, 89, 88, 88),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 14),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
height: 40,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
p.lastname.titleCase,
|
||||
style: const TextStyle(color: Colors.white),
|
||||
// overflow: TextOverflow.fade,
|
||||
textAlign: TextAlign.start,
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
p.firstname.titleCase,
|
||||
style: const TextStyle(color: Colors.white),
|
||||
textAlign: TextAlign.start,
|
||||
// overflow: TextOverflow.fade,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
const Text('STATUS')
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
@ -80,7 +125,14 @@ class _PersonsPageState extends State<PersonsPage> {
|
||||
child: Scaffold(
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () {},
|
||||
onPressed: () async {
|
||||
final p = await showPerson(context);
|
||||
if (!p.id.isZero) {
|
||||
setState(() {
|
||||
persons.add(p);
|
||||
});
|
||||
}
|
||||
},
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
appBar: AppBar(
|
||||
|
@ -1,9 +1,5 @@
|
||||
import 'package:app/gapi/client.dart';
|
||||
import 'package:app/pages_old/start_page.dart';
|
||||
import 'package:app/pb/account_info.pb.dart';
|
||||
import 'package:app/pb/rpc_get_account_info.pb.dart';
|
||||
import 'package:app/widgets/background.dart';
|
||||
import 'package:app/widgets/bottom_bar.dart';
|
||||
import 'package:app/widgets/loading_widget.dart';
|
||||
import 'package:app/widgets/side_drawer.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -1,12 +1,9 @@
|
||||
import 'package:app/gapi/client.dart';
|
||||
import 'package:app/model/services/backend_service.dart';
|
||||
import 'package:app/pages_old/start_page.dart';
|
||||
import 'package:app/widgets/background.dart';
|
||||
import 'package:app/widgets/bottom_bar.dart';
|
||||
import 'package:app/widgets/loading_widget.dart';
|
||||
import 'package:app/widgets/side_drawer.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:grpc/grpc.dart';
|
||||
|
||||
// GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
|
||||
|
@ -1,11 +1,8 @@
|
||||
import 'package:app/gapi/client.dart';
|
||||
import 'package:app/pages_old/start_page.dart';
|
||||
import 'package:app/widgets/background.dart';
|
||||
import 'package:app/widgets/bottom_bar.dart';
|
||||
import 'package:app/widgets/loading_widget.dart';
|
||||
import 'package:app/widgets/side_drawer.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:grpc/grpc.dart';
|
||||
|
||||
class RegisterPage extends StatefulWidget {
|
||||
const RegisterPage({
|
||||
|
@ -1,9 +1,6 @@
|
||||
import 'package:app/gapi/client.dart';
|
||||
import 'package:app/model/apis/api_response.dart';
|
||||
import 'package:app/model/view_model/account_vm.dart';
|
||||
import 'package:app/pages_old/dashboard_page.dart';
|
||||
import 'package:app/pages_old/login_page.dart';
|
||||
import 'package:app/pages_old/register_page.dart';
|
||||
import 'package:app/pb/account.pb.dart';
|
||||
import 'package:app/widgets/background.dart';
|
||||
import 'package:app/widgets/bottom_bar.dart';
|
||||
|
@ -24,4 +24,10 @@ extension valString on String {
|
||||
bool get isValidPhone {
|
||||
return phoneRegExp.hasMatch(this);
|
||||
}
|
||||
|
||||
String get titleCase {
|
||||
return split(' ')
|
||||
.map((str) => str[0].toUpperCase() + str.substring(1))
|
||||
.join(' ');
|
||||
}
|
||||
}
|
||||
|
@ -7,10 +7,13 @@ class BottomNavigation extends StatelessWidget {
|
||||
required this.children,
|
||||
this.backgroundColor,
|
||||
this.iconColor,
|
||||
this.hideMenu,
|
||||
}) {
|
||||
hideMenu ??= false;
|
||||
backgroundColor ??= Colors.black;
|
||||
}
|
||||
|
||||
bool? hideMenu;
|
||||
List<Widget> children;
|
||||
Color? backgroundColor;
|
||||
Color? iconColor;
|
||||
@ -32,14 +35,18 @@ class BottomNavigation extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
...children,
|
||||
Builder(builder: (context) {
|
||||
return IconButton(
|
||||
onPressed: () => Scaffold.of(context).openDrawer(),
|
||||
icon: const Icon(
|
||||
Icons.menu,
|
||||
color: Colors.white,
|
||||
));
|
||||
}),
|
||||
if (!hideMenu!)
|
||||
Builder(
|
||||
builder: (context) {
|
||||
return IconButton(
|
||||
onPressed: () => Scaffold.of(context).openDrawer(),
|
||||
icon: const Icon(
|
||||
Icons.menu,
|
||||
color: Colors.white,
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -110,6 +110,11 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
flutter_localizations:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
@ -155,6 +160,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: intl
|
||||
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.18.1"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -33,6 +33,8 @@ dependencies:
|
||||
collection: ^1.15.0-nullsafety.4
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
@ -43,6 +45,7 @@ dependencies:
|
||||
path: ^1.8.3
|
||||
fixnum: ^1.1.0
|
||||
provider: ^6.0.5
|
||||
intl: ^0.18.1
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^2.0.0
|
||||
|
Loading…
x
Reference in New Issue
Block a user