2024-12-07

This commit is contained in:
reinjens 2024-12-07 20:53:13 +01:00
parent ec2cf3d860
commit 46fbb8bbf6
38 changed files with 8225 additions and 5389 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 KiB

View file

@ -1,5 +1,6 @@
import 'package:ambito/src/config/config.dart';
import 'package:ambito/src/consts/consts.dart';
import 'package:ambito/src/entity/enums/enum_details_type.dart';
import 'package:ambito/src/entity/lists/list_measure.dart';
import 'package:ambito/src/entity/lists/list_repository.dart';
import 'package:ambito/src/packages/ambito_api/base_api.dart';
@ -8,6 +9,7 @@ import 'package:ambito/src/packages/ambito_notifier/notifier/theme_manager.dart'
import 'package:ambito/src/packages/ambito_sharedprefs/ambito_sharedprefs.dart';
import 'package:ambito/src/pages/calendar/calendar_page.dart';
import 'package:ambito/src/pages/calendar/calendar_page_year.dart';
import 'package:ambito/src/pages/dashboard/areas/dashboard_areas_page.dart';
import 'package:ambito/src/pages/dashboard/dashboard_page.dart';
import 'package:ambito/src/pages/error/error_page.dart';
import 'package:ambito/src/pages/measure/categories/measure_categories_page.dart';
@ -41,7 +43,8 @@ void main() async {
BaseApi().getContent('funding_program'),
//BaseApi().getContent('location_requirements'),
//BaseApi().getContent('reference_implementation'),
//BaseApi().getContent('business'),
BaseApi().getContent('business'),
BaseApi().getContent('area'),
//BaseApi().getContent('service_provider'),
//BaseApi().getContent('service_provider_contact_person'),
//BaseApi().getContent('material'),
@ -132,35 +135,42 @@ class Ambito extends StatelessWidget {
GetPage(
name: '/dashboard',
page: () => DashboardPage(
businessId: prefs.getInt('currentUser') ?? 22,
businessId: prefs.getInt('currentUser') ?? 100,
userId: 0,
),
),
GetPage(
name: '/dashboard/meine-massnahmen',
page: () => DashboardPage(
businessId: prefs.getInt('currentUser') ?? 22,
businessId: prefs.getInt('currentUser') ?? 100,
userId: 0,
),
),
GetPage(
name: '/dashboard/urkunde',
page: () => DashboardPage(
businessId: prefs.getInt('currentUser') ?? 22,
businessId: prefs.getInt('currentUser') ?? 100,
userId: 0,
),
),
GetPage(
name: '/dashboard/flaechen',
page: () => DashboardPage(
businessId: prefs.getInt('currentUser') ?? 22,
page: () => DashboardAreasPage(
businessId: prefs.getInt('currentUser') ?? 100,
userId: 0,
),
),
GetPage(
name: '/dashboard/flaechen/:index',
page: () => DashboardAreasPage(
businessId: prefs.getInt('currentUser') ?? 100,
userId: 0,
),
),
GetPage(
name: '/dashboard/stammdaten',
page: () => DashboardPage(
businessId: prefs.getInt('currentUser') ?? 22,
businessId: prefs.getInt('currentUser') ?? 100,
userId: 0,
),
),
@ -169,8 +179,9 @@ class Ambito extends StatelessWidget {
page: () => const MeasureCreatePage(),
),
GetPage(
name: '/massnahme/:id',
page: () => const MeasureDetailPage(),
name: '/massnahme/',
page: () => const MeasureDetailPage(
id: 0, type: EnumDetailsType.measure),
),
GetPage(
name: '/error',

View file

@ -45,8 +45,11 @@ const BreakpointConfiguration myBreakpoints = BreakpointConfiguration(
xxl: null,
);
const googleApiKey = 'AIzaSyAb2d7gn5CLWnVZTaSapRYHjnZapSP9BQM';
const baserowToken = 'TFxO7vzBLVRCu9I3VMoHmTuCvSu8aCDi';
const baserowIds = {
"area": 403344,
"measure": 328253,
"measure_general": 396946,
"measure_details": 342622,

View file

@ -14,5 +14,5 @@ class IdValue {
factory IdValue.fromJson(Map<String, dynamic> json) =>
_$IdValueFromJson(json);
Map<String, dynamic> toJson() => _$IdValueToJson(this);
int? toJson() => id;
}

View file

@ -15,5 +15,5 @@ class IdValueColor {
factory IdValueColor.fromJson(Map<String, dynamic> json) =>
_$IdValueColorFromJson(json);
Map<String, dynamic> toJson() => _$IdValueColorToJson(this);
int? toJson() => id;
}

View file

@ -15,5 +15,5 @@ class IdValueMix {
factory IdValueMix.fromJson(Map<String, dynamic> json) =>
_$IdValueMixFromJson(json);
Map<String, dynamic> toJson() => _$IdValueMixToJson(this);
int? toJson() => id;
}

View file

@ -0,0 +1,61 @@
import 'dart:convert';
import 'package:ambito/src/entity/base_entity.dart';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:isar/isar.dart';
import 'package:json_annotation/json_annotation.dart';
import '../../consts/consts.dart';
import '../_general/id_value/id_value.dart';
part 'area.g.dart';
@JsonSerializable(explicitToJson: true)
@collection
class Area extends BaseEntity with EntityWithId {
Area();
@JsonKey(name: 'field_3077525')
String? name;
@JsonKey(name: 'field_3077543')
List<IdValue>? business;
@JsonKey(name: 'field_3077526')
String? description;
@JsonKey(name: 'field_3077544')
String? size;
@JsonKey(name: 'field_3077545')
String? polygon;
factory Area.fromJson(Map<String, dynamic> json) => _$AreaFromJson(json);
Map<String, dynamic> toJson() => _$AreaToJson(this);
}
extension AreaExtension on Area {
Polygon? toPolygon() {
if (polygon == null) {
return null;
}
final json = jsonDecode(polygon!);
List<LatLng> points = [];
for (final point in json) {
points.add(LatLng(point[0], point[1]));
}
logger.d(points.toString());
return Polygon(
polygonId: PolygonId(
id.toString(),
),
strokeColor: const Color(0xFF60845E),
fillColor: const Color(0xff87A34E).withOpacity(.7),
strokeWidth: 2,
points: points,
);
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,138 @@
import 'package:ambito/src/entity/area/area.dart';
import 'package:ambito/src/widgets/dialogs/delete_entity/delete_entity_dialog.dart';
import 'package:ambito/src/widgets/dialogs/edit_entity_dialog/edit_entity_dialog.dart';
import 'package:ambito/src/widgets/dialogs/edit_field_dialog/edit_field_dialog.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:isar/isar.dart';
import 'package:syncfusion_flutter_datagrid/datagrid.dart';
import '../../packages/ambito_theme/ambito_theme.dart';
class AreaDataSource extends DataGridSource {
AreaDataSource(
{required List<Area> areas, required BuildContext this.context}) {
dataGridRows = areas
.map<DataGridRow>(
(dataGridRow) => DataGridRow(
cells: [
DataGridCell<int>(
columnName: 'id',
value: dataGridRow.id,
),
DataGridCell<String>(
columnName: 'name',
value: dataGridRow.name,
),
DataGridCell<String>(
columnName: 'size',
value: dataGridRow.size,
),
DataGridCell<String>(
columnName: 'description',
value: dataGridRow.description,
),
const DataGridCell(
columnName: 'action',
value: '',
),
],
),
)
.toList();
}
final BuildContext context;
List<DataGridRow> dataGridRows = [];
@override
List<DataGridRow> get rows => dataGridRows;
@override
DataGridRowAdapter? buildRow(DataGridRow row) {
final AmbitoTheme theme = getTheme(context);
final id = row.getCells().firstOrNull?.value;
NumberFormat numberFormat = NumberFormat.decimalPattern('de');
return DataGridRowAdapter(
cells: row.getCells().map<Widget>(
(dataGridCell) {
if (dataGridCell.columnName == 'action') {
return Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Row(
children: [
IconButton(
onPressed: () => EditEntityDialog.show(
context: context,
type: 'area',
id: id,
),
icon: Icon(
Icons.edit_outlined,
color: theme.currentColorScheme.primary,
),
),
IconButton(
onPressed: () => DeleteEntityDialog.show(
context: context,
type: 'area',
id: id,
),
icon: Icon(
Icons.delete_outline,
color: theme.currentColorScheme.primary,
),
),
],
),
);
}
if (dataGridCell.columnName == 'description') {
return Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: IconButton(
onPressed: () => EditFieldDialog.show(
context: context,
type: 'area_notice',
id: id,
),
icon: Icon(
Icons.description_outlined,
color: theme.currentColorScheme.primary,
),
),
);
}
if (dataGridCell.columnName == 'size') {
return Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Text(
'${numberFormat.format(float.parse(dataGridCell.value.toString()))}',
overflow: TextOverflow.ellipsis,
style: theme.bodyMedium.copyWith(
color: theme.currentColorScheme.primary,
),
),
);
}
return Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Text(
dataGridCell.value.toString(),
overflow: TextOverflow.ellipsis,
style: theme.bodyMedium.copyWith(
color: theme.currentColorScheme.primary,
),
),
);
},
).toList());
}
}

View file

@ -0,0 +1,21 @@
import 'package:ambito/src/entity/area/area.dart';
import 'package:ambito/src/packages/ambito_db/base_db.dart';
import 'package:isar/isar.dart';
import '../../consts/consts.dart';
class AreaRepository extends BaseDB {
@override
IsarCollection collection = isar.areas;
List<Area> getAreasForBusiness(int id) {
final all = isar.areas.where().findAll();
List<Area> areas = [];
for (final Area area in all) {
if (area.business!.any((ele) => ele.id == id)) {
areas.add(area);
}
}
return areas;
}
}

View file

@ -9,62 +9,70 @@ part 'business.g.dart';
class Business extends BaseEntity with EntityWithId {
Business();
@JsonKey(name: 'Modellbetrieb')
@JsonKey(name: 'field_2404114')
@Index()
String? name;
@JsonKey(name: 'Ursprungsgebiet')
IdValueColor? areaOfOrigin;
@JsonKey(name: 'Ansprechpartner')
String? businessOwner;
@JsonKey(name: 'Feld 6')
String? businessEmail;
@JsonKey(name: 'Feld 7')
String? businessPhone;
@JsonKey(name: 'Feld 8')
String? contactName;
@JsonKey(name: 'Feld 9')
String? contactPosition;
@JsonKey(name: 'Feld 10')
String? contactEmail;
@JsonKey(name: 'Feld 11')
String? contactPhone;
@JsonKey(name: 'Feld 12')
String? contact2Name;
@JsonKey(name: 'Feld 13')
String? contact2Position;
@JsonKey(name: 'Feld 14')
String? contact2Email;
@JsonKey(name: 'Feld 15')
String? contact2Phone;
@JsonKey(name: 'Feld 16')
String? contact3Name;
@JsonKey(name: 'Feld 17')
String? contact3Email;
@JsonKey(name: 'Feld 18')
String? addressComplete;
@JsonKey(name: 'Feld 19')
String? addressStreet;
@JsonKey(name: 'Feld 20')
String? addressPostalCode;
@JsonKey(name: 'Feld 21')
String? addressCity;
@JsonKey(name: 'Feld 22')
String? addressRegion;
@JsonKey(name: 'Feld 23')
String? addressFederalState;
@JsonKey(name: 'Feld 24')
String? unknown;
@JsonKey(name: 'Feld 25')
String? businessType;
@JsonKey(name: 'Region')
@JsonKey(name: 'field_3021727')
IdValueColor? businessType;
@JsonKey(name: 'field_2404115')
IdValueColor? businessOrigin;
@JsonKey(name: 'field_2404155')
List<IdValue>? referenceImplementation;
@JsonKey(name: 'field_2404143')
String? region;
@JsonKey(name: 'Umgesetze_Maßnahmen')
List<IdValueColor>? measures;
@JsonKey(name: '05 Referenzumsetzung')
List<IdValueColor>? referenceImplementation;
@JsonKey(name: '07 Erfahrungsbericht Umsetuzung')
List<IdValueColor>? experienceReport;
@JsonKey(name: 'field_2404117')
String? businessContact;
@JsonKey(name: 'field_2404119')
String? businessEmail;
@JsonKey(name: 'field_2404120')
String? businessPhone;
@JsonKey(name: 'field_2404121')
String? contact1Name;
@JsonKey(name: 'field_2404122')
String? contact1Position;
@JsonKey(name: 'field_2404123')
String? contact1Email;
@JsonKey(name: 'field_2404124')
String? contact1Phone;
@JsonKey(name: 'field_2404125')
String? contact2Name;
@JsonKey(name: 'field_2404126')
String? contact2Position;
@JsonKey(name: 'field_2404127')
String? contact2Email;
@JsonKey(name: 'field_2404128')
String? contact2Phone;
@JsonKey(name: 'field_2404129')
String? contact3Name;
@JsonKey(name: 'field_2404130')
String? contact3Email;
@JsonKey(name: 'field_2404131')
String? addressComplete;
@JsonKey(name: 'field_2404132')
String? addressStreet;
@JsonKey(name: 'field_2404133')
String? addressPostalCode;
@JsonKey(name: 'field_2404134')
String? addressCity;
@JsonKey(name: 'field_2404135')
String? addressRegion;
@JsonKey(name: 'field_2404136')
String? addressFederalState;
@JsonKey(name: 'field_2404138')
String? businessBio;
@JsonKey(name: 'field_2428872')
List<IdValue>? experienceReport;
factory Business.fromJson(Map<String, dynamic> json) =>
_$BusinessFromJson(json);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1 @@
enum EnumDetailsType { measure, type }

View file

@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
class ListDisplay {
ListDisplay();
int? id;
String? title;
String? description;
Widget? image;

View file

@ -43,6 +43,7 @@ class ListMeasureSingle extends BaseEntity with EntityWithId {
extension ListMeasureExtension on ListMeasureSingle {
ListDisplay asListDisplay() {
return ListDisplay()
..id = toJson()['id']
..title = name
..description = description
..image = getImage()

View file

@ -52,6 +52,7 @@ extension ListMeasureTypeGroupCategoryExtension
.isNotEmpty;
return ListDisplay()
..id = toJson()['id']
..title = measureType
..description = description
..image = getImage()

View file

@ -1,6 +1,7 @@
import 'dart:convert';
import 'package:ambito/main.dart';
import 'package:ambito/src/entity/area/area_repository.dart';
import 'package:ambito/src/entity/entities.dart';
import 'package:ambito/src/entity/measure/details/measure_details.dart';
import 'package:ambito/src/entity/measure/details/measure_details_repository.dart';
@ -11,6 +12,8 @@ import 'package:ambito/src/packages/ambito_api/restclient.dart';
import 'package:get/get.dart';
import '../../config/config.dart';
import '../../entity/area/area.dart';
import '../../entity/business/business_repository.dart';
import '../../entity/funding_program/funding_program_repository.dart';
import '../../entity/measure/measure_repository.dart';
@ -22,6 +25,61 @@ class BaseApi {
tables = baserowIds;
}
_getMap() {
return {
'area': AreaRepository().put,
'business': BusinessRepository().put,
'funding_program': FundingProgramRepository().put,
'measure_types': MeasureRepository().put,
'measure_general': MeasureGeneralRepository().put,
'measure_details': MeasureDetailsRepository().put,
'measure_types_details': MeasureTypesDetailsRepository().put,
};
}
Future<bool> patchContent(String table, int id, dynamic body) async {
init();
int tableId = tables[table] ?? 0;
if (tableId <= 0) return false;
var response = await RestClient().patch('$tableId/$id/', body);
if (response.statusCode != 200) Get.toNamed('/error');
return true;
}
Future<bool> deleteContent(String table, int id) async {
init();
int tableId = tables[table] ?? 0;
if (tableId <= 0) return false;
var response = await RestClient().delete('$tableId/$id/');
if (response.statusCode != 200) Get.toNamed('/error');
return true;
}
Future<bool> postContent(String table, dynamic body) async {
init();
int tableId = tables[table] ?? 0;
if (tableId <= 0) return false;
final repositoryMap = _getMap();
var response = await RestClient().post('$tableId/', body);
if (response.statusCode != 200) Get.toNamed('/error');
var model = _createModelFromJson(table, jsonDecode(response.body));
var repositoryFunction = repositoryMap[table];
if (repositoryFunction == null) return false;
repositoryFunction(model);
return true;
}
Future<bool> getContent(String table) async {
init();
int tableId = tables[table] ?? 0;
@ -36,18 +94,20 @@ class BaseApi {
var results = json['results'];
final repositoryMap = {
//'tree_type': TreeTypeRepository().put,
//'measure': MeasureRepository().putMeasureGroups,
'area': AreaRepository().put,
'business': BusinessRepository().put,
'funding_program': FundingProgramRepository().put,
'measure_types': MeasureRepository().put,
'measure_general': MeasureGeneralRepository().put,
'measure_details': MeasureDetailsRepository().put,
'measure_types_details': MeasureTypesDetailsRepository().put,
//'measure_combination': MeasureCombinationRepository().put,
//'organism': OrganismRepository().put,
'funding_program': FundingProgramRepository().put,
//'location_requirements': LocationRequirementsRepository().put,
//'reference_implementation': ReferenceImplementationRepository().put,
//'business': BusinessRepository().put,
//'tree_type': TreeTypeRepository().put,
//'measure': MeasureRepository().putMeasureGroups,
//'service_provider': ServiceProviderRepository().put,
//'service_provider_contact_person':
// ServiceProviderContactPersonRepository().put,
@ -70,6 +130,10 @@ class BaseApi {
dynamic _createModelFromJson(String table, Map<String, dynamic> json) {
switch (table) {
case 'area':
return Area.fromJson(json);
case 'business':
return Business.fromJson(json);
case 'measure_types':
final measureType = MeasureTypes.fromJson(json);
listMeasures.add(measureType.toListMeasure());

View file

@ -127,6 +127,7 @@ class RestClient {
final String uriString = '$baseUrl$endpoint';
final Uri uri = Uri.parse(uriString);
final Map<String, String> headers = _getHeaders();
logger.d(jsonEncode(body));
return _performRequest(
() {
return _client.patch(

View file

@ -1,3 +1,4 @@
import 'package:ambito/src/entity/area/area.dart';
import 'package:ambito/src/entity/entities.dart';
import 'package:ambito/src/entity/lists/list_measure.dart';
import 'package:ambito/src/entity/lists/list_measure_single.dart';
@ -25,6 +26,7 @@ class AmbitoIsarDB {
const engine = kIsWeb ? IsarEngine.sqlite : IsarEngine.isar;
isar = Isar.open(
schemas: [
AreaSchema,
ApprovalRequirementSchema,
BusinessSchema,
ExperienceReportSchema,
@ -82,4 +84,10 @@ abstract class BaseDB {
List<dynamic> getAll() {
return collection.where().findAll();
}
delete(int id) {
isar.write((isar) {
collection.delete(id);
});
}
}

View file

@ -77,7 +77,7 @@ abstract class AmbitoTheme {
onSecondary: Color(0xFFFFFFFF),
secondaryContainer: Color(0xFFD2E5F4),
onSecondaryContainer: Color(0xFF0A1D28),
tertiary: Color(0xFFD9D9D9),
tertiary: Color(0xFFf5f5f5),
onTertiary: Color(0xFFFFFFFF),
tertiaryContainer: Color(0xFFB8F483),
onTertiaryContainer: Color(0xFF0D2000),

View file

@ -0,0 +1,634 @@
import 'package:ambito/src/entity/_general/id_value/id_value.dart';
import 'package:ambito/src/entity/area/area_datasource.dart';
import 'package:ambito/src/entity/area/area_repository.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:google_geocoding_api/google_geocoding_api.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:screen_breakpoints/screen_breakpoints.dart';
import 'package:syncfusion_flutter_core/theme.dart';
import 'package:syncfusion_flutter_datagrid/datagrid.dart';
import 'package:toggle_switch/toggle_switch.dart';
import '../../../config/config.dart';
import '../../../consts/consts.dart';
import '../../../entity/area/area.dart';
import '../../../entity/business/business.dart';
import '../../../packages/ambito_api/base_api.dart';
import '../../../packages/ambito_theme/ambito_theme.dart';
import '../../../widgets/appbar/ambito_appbar.dart';
import '../../../widgets/map/map_widget.dart';
import '../../../widgets/map/marker_generator.dart';
import '../../../widgets/page/base_page.dart';
class DashboardAreasPage extends StatefulWidget {
const DashboardAreasPage(
{super.key, required this.businessId, required this.userId});
final int businessId;
final int userId;
@override
State<StatefulWidget> createState() => DashboardAreasPageState();
}
class DashboardAreasPageState extends State<DashboardAreasPage> {
Widget mapWidget = const SizedBox();
final api = GoogleGeocodingApi(googleApiKey, isLogged: false);
Set<Marker> markers = {};
Set<Polygon> polygons = {};
List<Area> areas = [];
Business? business;
int display = 0;
@override
void initState() {
BaseApi().getContent('business').then((_) {
business = isar.business.get(widget.businessId);
display = int.tryParse(Get.parameters['index'] ?? '0') ?? 0;
api
.search(business!.addressComplete!, language: 'de')
.then((GoogleGeocodingResponse response) {
MarkerGenerator(64)
.createBitmapDescriptorFromIconData(
Icons.home,
Colors.white,
const Color(0xFF60845E),
const Color(0xff87A34E),
)
.then((
BitmapDescriptor bitMapDescriptor,
) {
final prettyAddress = response.results.firstOrNull?.mapToPretty();
areas = AreaRepository().getAreasForBusiness(widget.businessId);
for (Area area in areas) {
final polygon = area.toPolygon();
if (polygon != null) {
setState(() {
polygons.add(polygon);
});
}
}
setState(() {
markers.add(
Marker(
markerId: MarkerId(business!.name!),
position: LatLng(
prettyAddress?.latitude ?? 0,
prettyAddress?.longitude ?? 0,
),
icon: bitMapDescriptor,
),
);
mapWidget = MapWidget(
markers: markers,
polygons: polygons,
);
});
});
});
});
super.initState();
}
@override
Widget build(BuildContext context) {
final AmbitoTheme theme = getTheme(context);
setState(() {});
areas = AreaRepository().getAreasForBusiness(widget.businessId);
return BasePage().getPage(
context,
SingleChildScrollView(
child: Align(
alignment: Alignment.topCenter,
child: Padding(
padding: context.breakpoint.padding,
child: SizedBox(
width: 1152,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
theme.verticalSpacer,
Text(
'Flächen',
style: theme.titleMedium,
),
theme.verticalSpacer,
Row(
children: [
ToggleSwitch(
initialLabelIndex: display,
totalSwitches: 2,
minWidth: 200,
borderWidth: 1,
labels: const ['Karte', 'Liste'],
activeBgColor: [theme.currentColorScheme.secondary],
borderColor: [theme.currentColorScheme.secondary],
inactiveBgColor: Colors.white,
activeFgColor: theme.currentColorScheme.onSecondary,
customTextStyles: [theme.bodyMedium],
onToggle: (index) {
setState(() {
display = index ?? 0;
});
},
),
const Spacer(),
OutlinedButton(
style: OutlinedButton.styleFrom(
minimumSize: const Size(200, 50),
shape: RoundedRectangleBorder(
side: const BorderSide(
color: Colors.blue,
width: 1,
style: BorderStyle.solid),
borderRadius: BorderRadius.circular(
10,
),
),
backgroundColor: theme.currentColorScheme.secondary,
foregroundColor:
theme.currentColorScheme.onSecondary,
side: BorderSide(
width: 1,
color: theme.currentColorScheme.secondary),
),
onPressed: () async {
final TextEditingController _controllerName =
TextEditingController();
final TextEditingController _controllerSize =
TextEditingController();
final TextEditingController _controllerDescription =
TextEditingController();
await showDialog<String>(
context: context,
builder: (BuildContext context) =>
Dialog.fullscreen(
child: Scaffold(
appBar: AmbitoAppbar(
links: const ['dashboard', 'massnahmen'],
breakpoint:
Breakpoint.fromContext(context),
theme: theme),
body: BreakpointBuilder(
builder: (
context,
breakpoint,
configuration,
) {
return SingleChildScrollView(
child: Center(
child: SizedBox(
width:
Breakpoint.fromContext(context)
.width,
child: Padding(
padding:
const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment:
MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
theme.verticalSpacer,
Text(
'Fläche bearbeiten',
style: theme.headlineMedium
.copyWith(
color: theme
.currentColorScheme
.onSurface,
),
),
theme.verticalSpacer,
Align(
alignment:
Alignment.centerRight,
child: Row(
mainAxisAlignment:
MainAxisAlignment.end,
children: [
OutlinedButton(
style: OutlinedButton
.styleFrom(
minimumSize:
const Size(
200, 50),
shape:
RoundedRectangleBorder(
side: BorderSide(
color: redColors[
'primary']!,
width: 1,
style:
BorderStyle
.solid,
),
borderRadius:
BorderRadius
.circular(
10,
),
),
backgroundColor: theme
.currentColorScheme
.surface
.withOpacity(
.1),
foregroundColor: theme
.currentColorScheme
.primary,
side: BorderSide(
width: 1,
color: theme
.currentColorScheme
.primary,
),
),
onPressed: () {
Navigator.pop(
context);
},
child: Text(
'Abbrechen',
style: theme
.bodyMedium,
),
),
theme.horizontalSpacer,
OutlinedButton(
onHover: (hover) {},
style: OutlinedButton
.styleFrom(
minimumSize:
const Size(
200, 50),
shape:
RoundedRectangleBorder(
side: BorderSide(
color: redColors[
'primary']!,
width: 1,
style:
BorderStyle
.solid,
),
borderRadius:
BorderRadius
.circular(
10,
),
),
backgroundColor: theme
.currentColorScheme
.secondary,
foregroundColor: theme
.currentColorScheme
.onPrimary,
side: BorderSide(
width: 1,
color: theme
.currentColorScheme
.secondary,
),
),
onPressed: () async {
Area area = Area()
..id = isar.areas
.autoIncrement()
..description =
_controllerDescription
.value
.text
..business = [
IdValue()
..id = widget
.businessId
..value =
business!
.name!
]
..name =
_controllerName
.value
.text
..size =
_controllerSize
.value
.text;
await BaseApi()
.postContent(
'area',
area.toJson());
await BaseApi()
.getContent(
'business');
setState(() {
areas = AreaRepository()
.getAreasForBusiness(
widget
.businessId);
logger.d(
areas.length);
});
Navigator.pop(
context);
},
child: Text(
'Speichern',
style: theme
.bodyMedium,
),
),
],
),
),
theme.verticalSpacer,
Card(
elevation: 0,
color: theme
.currentColorScheme
.tertiary,
child: Padding(
padding:
EdgeInsets.all(20),
child: Column(
crossAxisAlignment:
CrossAxisAlignment
.start,
children: [
Text(
'Allgemein',
style: theme
.headlineSmall,
),
theme.verticalSpacer,
Row(
children: [
Expanded(
child:
TextField(
controller:
_controllerName,
maxLines: 1,
decoration:
InputDecoration(
labelText:
'Bezeichnung',
isDense:
true,
hintText:
'Bezeichnung',
filled:
true,
fillColor:
Colors
.white,
hoverColor:
Colors
.white,
border:
OutlineInputBorder(
borderSide:
BorderSide(
color: redColors[
'primary']!,
width:
1,
style: BorderStyle
.solid,
),
borderRadius:
BorderRadius.circular(
8),
),
),
),
),
theme
.horizontalSpacer,
Expanded(
child:
TextField(
controller:
_controllerSize,
maxLines: 1,
decoration:
InputDecoration(
suffixText:
'',
labelText:
'Größe',
isDense:
true,
hintText:
'Größe',
filled:
true,
fillColor:
Colors
.white,
hoverColor:
Colors
.white,
border:
OutlineInputBorder(
borderSide:
BorderSide(
color: redColors[
'primary']!,
width:
1,
style: BorderStyle
.solid,
),
borderRadius:
BorderRadius.circular(
8),
),
),
),
),
],
),
theme.verticalSpacer,
TextField(
controller:
_controllerDescription,
maxLines: 5,
decoration:
InputDecoration(
labelText:
'Notizen',
isDense: true,
hintText:
'Notizen',
filled: true,
fillColor:
Colors.white,
hoverColor:
Colors.white,
border:
OutlineInputBorder(
borderSide:
BorderSide(
color: redColors[
'primary']!,
width: 1,
style:
BorderStyle
.solid,
),
borderRadius:
BorderRadius
.circular(
8),
),
),
),
],
),
),
),
theme.verticalSpacer,
],
),
),
),
),
);
},
),
),
),
);
Get.offAndToNamed('/dashboard/flaechen/1');
},
child: Text(
'Neue Fläche',
style: theme.bodyMedium,
))
],
),
theme.verticalSpacer,
(display == 0) ? mapWidget : gridWidget(areas),
theme.verticalSpacer,
],
),
),
),
),
),
);
}
Widget gridWidget(List<Area> areas) {
final AmbitoTheme theme = getTheme(context);
return SizedBox(
width: 1152,
child: SfDataGridTheme(
data: SfDataGridThemeData(
headerColor: theme.currentColorScheme.primaryContainer,
sortIcon: Icon(
Icons.keyboard_arrow_down,
color: theme.currentColorScheme.primary,
),
),
child: SfDataGrid(
allowSorting: true,
source: AreaDataSource(areas: areas, context: context),
columnWidthMode: ColumnWidthMode.fill,
columns: [
GridColumn(
visible: false,
columnName: 'id',
label: Container(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
alignment: Alignment.centerRight,
child: Text(
'ID',
overflow: TextOverflow.ellipsis,
style: theme.bodyMedium.copyWith(
color: theme.currentColorScheme.primary,
),
),
),
),
GridColumn(
columnName: 'name',
label: Container(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
alignment: Alignment.centerLeft,
child: Text(
'Bezeichnung',
overflow: TextOverflow.ellipsis,
style: theme.bodyMedium.copyWith(
color: theme.currentColorScheme.primary,
),
),
),
),
GridColumn(
columnName: 'size',
maximumWidth: 200,
label: Container(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
alignment: Alignment.centerLeft,
child: Text(
'Größe',
overflow: TextOverflow.ellipsis,
style: theme.bodyMedium.copyWith(
color: theme.currentColorScheme.primary,
),
),
),
),
GridColumn(
columnName: 'description',
maximumWidth: 100,
allowSorting: false,
label: Container(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
alignment: Alignment.centerLeft,
child: Text(
'Notiz',
overflow: TextOverflow.ellipsis,
style: theme.bodyMedium.copyWith(
color: theme.currentColorScheme.primary,
),
),
),
),
GridColumn(
columnName: 'action',
allowSorting: false,
maximumWidth: 150,
label: Container(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
alignment: Alignment.centerLeft,
child: Text(
'Aktion',
overflow: TextOverflow.ellipsis,
style: theme.bodyMedium.copyWith(
color: theme.currentColorScheme.primary,
),
),
),
),
],
),
),
);
}
}

View file

@ -1,11 +1,18 @@
import 'package:ambito/src/config/config.dart';
import 'package:ambito/src/entity/business/business.dart';
import 'package:ambito/src/packages/ambito_theme/ambito_theme.dart';
import 'package:ambito/src/widgets/map/map_widget.dart';
import 'package:ambito/src/widgets/map/marker_generator.dart';
import 'package:ambito/src/widgets/page/base_page.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:google_geocoding_api/google_geocoding_api.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:percent_indicator/linear_percent_indicator.dart';
import 'package:screen_breakpoints/screen_breakpoints.dart';
import '../../consts/consts.dart';
part 'layouts/normal_layout_dashboard_page.dart';
part 'layouts/small_layout_dashboard_page.dart';
part 'parts/parts_dashboard_page.dart';
@ -24,9 +31,49 @@ class DashboardPage extends StatefulWidget {
class DashboardPageState extends State<DashboardPage> {
List<Widget> cards = [];
Set<Marker> markers = {};
Widget mapWidget = CircularProgressIndicator();
final api = GoogleGeocodingApi(googleApiKey, isLogged: false);
@override
void initState() {
cards = [];
Business? business = isar.business.get(100);
api
.search(business!.addressComplete!, language: 'de')
.then((GoogleGeocodingResponse response) {
MarkerGenerator(64)
.createBitmapDescriptorFromIconData(
Icons.home,
Colors.white,
const Color(0xFF60845E),
const Color(0xff87A34E),
)
.then((
BitmapDescriptor bitMapDescriptor,
) {
final prettyAddress = response.results.firstOrNull?.mapToPretty();
setState(() {
markers.add(
Marker(
markerId: MarkerId(business.name!),
position: LatLng(
prettyAddress?.latitude ?? 0,
prettyAddress?.longitude ?? 0,
),
icon: bitMapDescriptor,
),
);
mapWidget = MapWidget(
markers: markers,
polygons: {},
);
});
});
});
super.initState();
}

View file

@ -13,7 +13,7 @@ extension NormalLayoutDashboardPage on DashboardPageState {
const Spacer(),
SizedBox(
width: 532,
child: _fundingPart(theme),
child: mapWidget,
),
],
);

View file

@ -1,5 +1,5 @@
import 'package:ambito/src/entity/entities.dart';
import 'package:ambito/src/entity/measure/measure_repository.dart';
import 'package:ambito/src/entity/enums/enum_details_type.dart';
import 'package:ambito/src/extensions/extensions.dart';
import 'package:animated_segmented_tab_control/animated_segmented_tab_control.dart';
import 'package:expansion_tile_card/expansion_tile_card.dart';
@ -17,7 +17,10 @@ import 'cards/_cards.dart';
final Map<String, GlobalKey<ExpansionTileCardState>> globalKeys = {};
class MeasureDetailPage extends AmbitoPage {
const MeasureDetailPage({super.key});
const MeasureDetailPage({required this.id, required this.type, super.key});
final EnumDetailsType type;
final int id;
@override
final String path = 'massnahme';
@ -29,7 +32,6 @@ class MeasureDetailPage extends AmbitoPage {
}
class MeasureDetailPageState extends State<MeasureDetailPage> {
late final String id;
final ScrollController scrollController = ScrollController();
bool showBackToTopButton = false;
final Map<String, GlobalKey<ExpansionTileCardState>> expansionKeys = {};
@ -42,10 +44,8 @@ class MeasureDetailPageState extends State<MeasureDetailPage> {
void initState() {
super.initState();
id = Get.parameters['id'] ?? '';
if (id.isNotEmpty) {
massnahme = MeasureRepository().get(int.parse(id)) as MeasureTypes;
}
//massnahme = MeasureRepository().get(int.parse(id)) as MeasureTypes;
//}
scrollController.addListener(() {
const showOffset = 10.0;

View file

@ -1,9 +1,11 @@
import 'package:ambito/src/config/config.dart';
import 'package:ambito/src/entity/enums/enum_details_type.dart';
import 'package:ambito/src/entity/funding_program/funding_program_repository.dart';
import 'package:ambito/src/entity/lists/list_display.dart';
import 'package:ambito/src/entity/lists/list_repository.dart';
import 'package:ambito/src/extensions/extensions.dart';
import 'package:ambito/src/pages/ambito_page.dart';
import 'package:ambito/src/pages/measure/detail/measure_detail_page.dart';
import 'package:ambito/src/widgets/form/fields/field_title.dart';
import 'package:ambito/src/widgets/form/form_widget.dart';
import 'package:ambito/src/widgets/form/form_widget_type.dart';
@ -13,6 +15,7 @@ import 'package:get/get.dart';
import 'package:highlight_text/highlight_text.dart';
import 'package:screen_breakpoints/screen_breakpoints.dart';
import '../../consts/consts.dart';
import '../../entity/measure/measure_repository.dart';
import '../../packages/ambito_notifier/notifier/filter_notifier.dart';
import '../../packages/ambito_theme/ambito_theme.dart';
@ -266,7 +269,15 @@ class MeasuresPageState extends State<MeasuresPage> {
Get.toNamed(
'/massnahmendatenbank/$filterCategory/$filterGroup/${measure.title!.toLowerCase().replaceUmlauts()}');
} else if (measure.isMeasure == true) {
Get.toNamed('/massnahme/${measure.title}');
logger.d(measure.id);
Get.to(
() => MeasureDetailPage(
id: measure.id!,
type: measure.isMeasure!
? EnumDetailsType.measure
: EnumDetailsType.type,
),
);
}
},
onHover: (hovered) {},

View file

@ -0,0 +1,52 @@
import 'package:flutter/material.dart';
import '../../packages/ambito_theme/ambito_theme.dart';
class WidgetOutlineButton extends StatelessWidget {
const WidgetOutlineButton({
super.key,
required this.onPressed,
required this.title,
required this.backgroundColor,
required this.foregroundColor,
required this.borderColor,
});
final VoidCallback onPressed;
final String title;
final Color backgroundColor;
final Color foregroundColor;
final Color borderColor;
@override
Widget build(BuildContext context) {
final AmbitoTheme theme = getTheme(context);
return OutlinedButton(
onHover: (hover) {},
style: OutlinedButton.styleFrom(
minimumSize: const Size(200, 50),
shape: RoundedRectangleBorder(
side: BorderSide(
color: borderColor,
width: 1,
style: BorderStyle.solid,
),
borderRadius: BorderRadius.circular(
10,
),
),
backgroundColor: backgroundColor,
foregroundColor: foregroundColor,
side: BorderSide(
width: 1,
color: borderColor,
),
),
onPressed: onPressed,
child: Text(
title,
style: theme.bodyMedium,
),
);
}
}

View file

@ -0,0 +1,85 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../entity/area/area_repository.dart';
import '../../../packages/ambito_api/base_api.dart';
import '../../../packages/ambito_theme/ambito_theme.dart';
import '../../buttons/outline_button.dart';
class DeleteAreaDialog extends StatelessWidget {
const DeleteAreaDialog({super.key, required this.id});
final int id;
@override
Widget build(BuildContext context) {
final AmbitoTheme theme = getTheme(context);
return Dialog(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(10.0),
),
),
child: SizedBox(
width: 800,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Fläche löschen',
style: theme.headlineSmall.copyWith(
color: theme.currentColorScheme.error,
),
),
theme.verticalSpacer,
Text(
'Möchten Sie die Fläche wirklich löschen?',
style: theme.bodyMedium,
),
Text(
'Diese wird auch in der Flächenliste entgültig gelöscht.',
style: theme.bodyMedium,
),
theme.verticalSpacerMax,
Align(
alignment: Alignment.centerRight,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
WidgetOutlineButton(
onPressed: () {
Navigator.pop(context);
},
title: 'Abbrechen',
backgroundColor: theme.currentColorScheme.tertiary,
foregroundColor: theme.currentColorScheme.error,
borderColor: theme.currentColorScheme.error,
),
theme.horizontalSpacer,
WidgetOutlineButton(
onPressed: () {
AreaRepository().delete(id);
BaseApi().deleteContent('area', id).then((_) {
Navigator.pop(context);
Get.offAndToNamed('/dashboard/flaechen/1');
});
},
title: 'Löschen',
backgroundColor: theme.currentColorScheme.error,
foregroundColor: theme.currentColorScheme.onError,
borderColor: theme.currentColorScheme.error,
),
],
),
),
],
),
),
),
);
}
}

View file

@ -0,0 +1,18 @@
import 'package:ambito/src/widgets/dialogs/delete_entity/delete_area_dialog.dart';
import 'package:flutter/material.dart';
class DeleteEntityDialog {
static Future<dynamic> show(
{required BuildContext context,
required String type,
required int id}) async {
Map<String, dynamic> dialogs = {
'area': DeleteAreaDialog,
};
return await showDialog<String>(
context: context,
builder: (BuildContext context) => DeleteAreaDialog(id: id),
);
}
}

View file

@ -0,0 +1,210 @@
import 'package:ambito/src/widgets/buttons/outline_button.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:isar/isar.dart';
import 'package:screen_breakpoints/screen_breakpoints.dart';
import '../../../consts/consts.dart';
import '../../../entity/area/area.dart';
import '../../../entity/area/area_repository.dart';
import '../../../packages/ambito_api/base_api.dart';
import '../../../packages/ambito_theme/ambito_theme.dart';
import '../../appbar/ambito_appbar.dart';
class EditAreaDialog extends StatelessWidget {
final TextEditingController _controllerName = TextEditingController();
final TextEditingController _controllerSize = TextEditingController();
final TextEditingController _controllerDescription = TextEditingController();
final int id;
EditAreaDialog({super.key, required this.id});
@override
Widget build(BuildContext context) {
final AmbitoTheme theme = getTheme(context);
Area? area = isar.areas.where().idEqualTo(id).findFirst();
if (area != null) {
_controllerName.text = area.name!;
_controllerDescription.text = area.description!;
_controllerSize.text = area.size!;
}
return Dialog.fullscreen(
child: Scaffold(
appBar: AmbitoAppbar(
links: const ['dashboard', 'massnahmen'],
breakpoint: Breakpoint.fromContext(context),
theme: theme),
body: BreakpointBuilder(
builder: (
context,
breakpoint,
configuration,
) {
return SingleChildScrollView(
child: Center(
child: SizedBox(
width: Breakpoint.fromContext(context).width,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
theme.verticalSpacer,
Text(
'Fläche bearbeiten',
style: theme.headlineMedium.copyWith(
color: theme.currentColorScheme.onSurface,
),
),
theme.verticalSpacer,
Align(
alignment: Alignment.centerRight,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
WidgetOutlineButton(
onPressed: () {
Navigator.pop(context);
},
title: 'Abbrechen',
backgroundColor: theme
.currentColorScheme.surface
.withOpacity(.1),
foregroundColor:
theme.currentColorScheme.primary,
borderColor: theme.currentColorScheme.primary,
),
theme.horizontalSpacer,
WidgetOutlineButton(
onPressed: () {
Area area = isar.areas.get(id)!;
area.description =
_controllerDescription.value.text;
area.name = _controllerName.value.text;
area.size = _controllerSize.value.text;
AreaRepository().put(area);
BaseApi()
.patchContent('area', id, area.toJson())
.then((_) {
Navigator.pop(context);
Get.offAndToNamed('/dashboard/flaechen/1');
});
},
title: 'Speichern',
backgroundColor:
theme.currentColorScheme.secondary,
foregroundColor:
theme.currentColorScheme.onPrimary,
borderColor: theme.currentColorScheme.secondary,
),
],
),
),
theme.verticalSpacer,
Card(
elevation: 0,
color: theme.currentColorScheme.tertiary,
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Allgemein',
style: theme.headlineSmall,
),
theme.verticalSpacer,
Row(
children: [
Expanded(
child: TextField(
controller: _controllerName,
maxLines: 1,
decoration: InputDecoration(
labelText: 'Bezeichnung',
isDense: true,
hintText: 'Bezeichnung',
filled: true,
fillColor: Colors.white,
hoverColor: Colors.white,
border: OutlineInputBorder(
borderSide: BorderSide(
color: redColors['primary']!,
width: 1,
style: BorderStyle.solid,
),
borderRadius:
BorderRadius.circular(8),
),
),
),
),
theme.horizontalSpacer,
Expanded(
child: TextField(
controller: _controllerSize,
maxLines: 1,
decoration: InputDecoration(
suffixText: '',
labelText: 'Größe',
isDense: true,
hintText: 'Größe',
filled: true,
fillColor: Colors.white,
hoverColor: Colors.white,
border: OutlineInputBorder(
borderSide: BorderSide(
color: redColors['primary']!,
width: 1,
style: BorderStyle.solid,
),
borderRadius:
BorderRadius.circular(8),
),
),
),
),
],
),
theme.verticalSpacer,
TextField(
controller: _controllerDescription,
maxLines: 5,
decoration: InputDecoration(
labelText: 'Notizen',
isDense: true,
hintText: 'Notizen',
filled: true,
fillColor: Colors.white,
hoverColor: Colors.white,
border: OutlineInputBorder(
borderSide: BorderSide(
color: redColors['primary']!,
width: 1,
style: BorderStyle.solid,
),
borderRadius: BorderRadius.circular(8),
),
),
),
],
),
),
),
theme.verticalSpacer,
],
),
),
),
),
);
},
),
),
);
}
}

View file

@ -0,0 +1,18 @@
import 'package:ambito/src/widgets/dialogs/edit_entity_dialog/edit_area_dialog.dart';
import 'package:flutter/material.dart';
class EditEntityDialog {
static Future<dynamic> show(
{required BuildContext context,
required String type,
required int id}) async {
Map<String, dynamic> dialogs = {
'area': EditAreaDialog,
};
return await showDialog<String>(
context: context,
builder: (BuildContext context) => EditAreaDialog(id: id),
);
}
}

View file

@ -0,0 +1,107 @@
import 'package:flutter/material.dart';
import 'package:isar/isar.dart';
import '../../../consts/consts.dart';
import '../../../entity/area/area.dart';
import '../../../entity/area/area_repository.dart';
import '../../../packages/ambito_api/base_api.dart';
import '../../../packages/ambito_theme/ambito_theme.dart';
import '../../buttons/outline_button.dart';
class EditAreaNoticeDialog extends StatelessWidget {
final TextEditingController _controllerDescription = TextEditingController();
final int id;
EditAreaNoticeDialog({super.key, required this.id});
@override
Widget build(BuildContext context) {
final AmbitoTheme theme = getTheme(context);
Area? area = isar.areas.where().idEqualTo(id).findFirst();
if (area != null) {
_controllerDescription.text = area.description!;
}
return Dialog(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(10.0),
),
),
child: SizedBox(
width: 800,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Notiz:',
style: theme.headlineSmall.copyWith(
color: theme.currentColorScheme.primary,
),
),
theme.verticalSpacer,
TextField(
controller: _controllerDescription,
maxLines: 5,
decoration: InputDecoration(
labelText: 'Notizen',
isDense: true,
hintText: 'Notizen',
border: OutlineInputBorder(
borderSide: BorderSide(
color: redColors['primary']!,
width: 1,
style: BorderStyle.solid,
),
borderRadius: BorderRadius.circular(8),
),
),
),
theme.verticalSpacer,
Align(
alignment: Alignment.centerRight,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
WidgetOutlineButton(
onPressed: () {
Navigator.pop(context);
},
title: 'Abbrechen',
backgroundColor:
theme.currentColorScheme.surface.withOpacity(.1),
foregroundColor: theme.currentColorScheme.primary,
borderColor: theme.currentColorScheme.primary,
),
theme.horizontalSpacer,
WidgetOutlineButton(
onPressed: () {
Area area = isar.areas.get(id)!;
area.description = _controllerDescription.value.text;
AreaRepository().put(area);
BaseApi()
.patchContent('area', id, area.toJson())
.then((_) {
Navigator.pop(context);
});
},
title: 'Speichern',
backgroundColor: theme.currentColorScheme.secondary,
foregroundColor: theme.currentColorScheme.onPrimary,
borderColor: theme.currentColorScheme.secondary,
),
],
),
),
],
),
),
),
);
}
}

View file

@ -0,0 +1,19 @@
import 'package:flutter/material.dart';
import 'edit_area_notice_dialog.dart';
class EditFieldDialog {
static Future<dynamic> show(
{required BuildContext context,
required String type,
required int id}) async {
Map<String, dynamic> dialogs = {
'area_notice': EditAreaNoticeDialog,
};
return await showDialog<String>(
context: context,
builder: (BuildContext context) => EditAreaNoticeDialog(id: id),
);
}
}

View file

@ -0,0 +1,25 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
Future<BitmapDescriptor> iconToBitmapDescriptor(IconData iconData) async {
final pictureRecorder = PictureRecorder();
final canvas = Canvas(pictureRecorder);
final textPainter = TextPainter(textDirection: TextDirection.ltr);
final iconStr = String.fromCharCode(iconData.codePoint);
textPainter.text = TextSpan(
text: iconStr,
style: TextStyle(
letterSpacing: 0.0,
fontSize: 48.0,
fontFamily: iconData.fontFamily,
color: Colors.red,
));
textPainter.layout();
textPainter.paint(canvas, const Offset(0.0, 0.0));
final picture = pictureRecorder.endRecording();
final image = await picture.toImage(48, 48);
final bytes = await image.toByteData(format: ImageByteFormat.png);
return BitmapDescriptor.bytes(bytes!.buffer.asUint8List());
}

View file

@ -0,0 +1,56 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
class MapWidget extends StatefulWidget {
const MapWidget({super.key, required this.markers, required this.polygons});
final Set<Marker>? markers;
final Set<Polygon>? polygons;
@override
State<StatefulWidget> createState() => MapWidgetState();
}
class MapWidgetState extends State<MapWidget> {
final Completer<GoogleMapController> _controller =
Completer<GoogleMapController>();
static CameraPosition _initialPos = const CameraPosition(
target: LatLng(0, 0),
zoom: 14.4746,
);
@override
void initState() {
if (widget.markers != null) {
setState(() {
_initialPos =
CameraPosition(target: widget.markers!.first.position, zoom: 14);
});
}
super.initState();
}
@override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: SizedBox(
width: double.infinity,
height: 525,
child: GoogleMap(
mapToolbarEnabled: true,
mapType: MapType.hybrid,
markers: widget.markers ?? {},
polygons: widget.polygons ?? {},
initialCameraPosition: _initialPos,
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
),
),
);
}
}

View file

@ -0,0 +1,76 @@
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
class MarkerGenerator {
final _markerSize;
double _circleStrokeWidth = 0;
double _circleOffset = 0;
double _outlineCircleWidth = 0;
double _fillCircleWidth = 0;
double _iconSize = 0;
double _iconOffset = 0;
MarkerGenerator(this._markerSize) {
_circleStrokeWidth = _markerSize / 10.0;
_circleOffset = _markerSize / 2;
_outlineCircleWidth = _circleOffset - (_circleStrokeWidth / 2);
_fillCircleWidth = _markerSize / 2;
final outlineCircleInnerWidth = _markerSize - (2 * _circleStrokeWidth);
_iconSize = sqrt(pow(outlineCircleInnerWidth, 2) / 2);
final rectDiagonal = sqrt(2 * pow(_markerSize, 2));
final circleDistanceToCorners =
(rectDiagonal - outlineCircleInnerWidth) / 2;
_iconOffset = sqrt(pow(circleDistanceToCorners, 2) / 2);
}
Future<BitmapDescriptor> createBitmapDescriptorFromIconData(IconData iconData,
Color iconColor, Color circleColor, Color backgroundColor) async {
final pictureRecorder = PictureRecorder();
final canvas = Canvas(pictureRecorder);
_paintCircleFill(canvas, backgroundColor);
_paintCircleStroke(canvas, circleColor);
_paintIcon(canvas, iconColor, iconData);
final picture = pictureRecorder.endRecording();
final image =
await picture.toImage(_markerSize.round(), _markerSize.round());
final bytes = await image.toByteData(format: ImageByteFormat.png);
return BitmapDescriptor.bytes(bytes!.buffer.asUint8List());
}
void _paintCircleFill(Canvas canvas, Color color) {
final paint = Paint()
..style = PaintingStyle.fill
..color = color;
canvas.drawCircle(
Offset(_circleOffset, _circleOffset), _fillCircleWidth, paint);
}
void _paintCircleStroke(Canvas canvas, Color color) {
final paint = Paint()
..style = PaintingStyle.stroke
..color = color
..strokeWidth = _circleStrokeWidth;
canvas.drawCircle(
Offset(_circleOffset, _circleOffset), _outlineCircleWidth, paint);
}
void _paintIcon(Canvas canvas, Color color, IconData iconData) {
final textPainter = TextPainter(textDirection: TextDirection.ltr);
textPainter.text = TextSpan(
text: String.fromCharCode(iconData.codePoint),
style: TextStyle(
letterSpacing: 0.0,
fontSize: _iconSize,
fontFamily: iconData.fontFamily,
color: color,
));
textPainter.layout();
textPainter.paint(canvas, Offset(_iconOffset, _iconOffset));
}
}

View file

@ -246,14 +246,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.8"
dart_earcut:
dependency: transitive
description:
name: dart_earcut
sha256: "41b493147e30a051efb2da1e3acb7f38fe0db60afba24ac1ea5684cee272721e"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
dart_style:
dependency: transitive
description:
@ -270,14 +262,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.7.0"
dio_cache_interceptor:
dependency: transitive
description:
name: dio_cache_interceptor
sha256: fb7905c0d12075d8786a6b63bffd64ae062d053f682cfaf28d145a2686507308
url: "https://pub.dev"
source: hosted
version: "3.5.0"
dio_web_adapter:
dependency: transitive
description:
@ -379,14 +363,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.4.1"
flutter_compass:
dependency: transitive
description:
name: flutter_compass
sha256: "1b4d7e6c95a675ec8482b5c9c9ccf1ebf0ced3dbec59dce28ad609da953de850"
url: "https://pub.dev"
source: hosted
version: "0.8.1"
flutter_dotenv:
dependency: "direct main"
description:
@ -432,30 +408,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_map:
dependency: "direct main"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_map
sha256: "2ecb34619a4be19df6f40c2f8dce1591675b4eff7a6857bd8f533706977385da"
name: flutter_plugin_android_lifecycle
sha256: "9b78450b89f059e96c9ebb355fa6b3df1d6b330436e0b885fb49594c41721398"
url: "https://pub.dev"
source: hosted
version: "7.0.2"
flutter_map_cache:
dependency: "direct main"
description:
name: flutter_map_cache
sha256: "47607b8d95ca791f0367d18955035d098faf80990e5e3bb0dbfa26271a6c2f43"
url: "https://pub.dev"
source: hosted
version: "1.5.1"
flutter_map_location_marker:
dependency: "direct main"
description:
name: flutter_map_location_marker
sha256: "1971d9ad7c8bedb15cb6a03598c0a3ad66ac6f7f165ad90d801bd5f636120f38"
url: "https://pub.dev"
source: hosted
version: "9.1.1"
version: "2.0.23"
flutter_rating:
dependency: "direct main"
description:
@ -570,6 +530,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.2.1"
google_geocoding_api:
dependency: "direct main"
description:
name: google_geocoding_api
sha256: "92080e6a0cfcd28b2a73454213e8d0c067342b237392a4431c2787ac10ffeca2"
url: "https://pub.dev"
source: hosted
version: "1.5.2"
google_maps:
dependency: transitive
description:
name: google_maps
sha256: "4d6e199c561ca06792c964fa24b2bac7197bf4b401c2e1d23e345e5f9939f531"
url: "https://pub.dev"
source: hosted
version: "8.1.1"
google_maps_flutter:
dependency: "direct main"
description:
name: google_maps_flutter
sha256: "209856c8e5571626afba7182cf634b2910069dc567954e76ec3e3fb37f5e9db3"
url: "https://pub.dev"
source: hosted
version: "2.10.0"
google_maps_flutter_android:
dependency: transitive
description:
name: google_maps_flutter_android
sha256: bccf64ccbb2ea672dc62a61177b315a340af86b0228564484b023657544a3fd5
url: "https://pub.dev"
source: hosted
version: "2.14.11"
google_maps_flutter_ios:
dependency: transitive
description:
name: google_maps_flutter_ios
sha256: "6f798adb0aa1db5adf551f2e39e24bd06c8c0fbe4de912fb2d9b5b3f48147b02"
url: "https://pub.dev"
source: hosted
version: "2.13.2"
google_maps_flutter_platform_interface:
dependency: transitive
description:
name: google_maps_flutter_platform_interface
sha256: a951981c22d790848efb9f114f81794945bc5c06bc566238a419a92f110af6cb
url: "https://pub.dev"
source: hosted
version: "2.9.5"
google_maps_flutter_web:
dependency: transitive
description:
name: google_maps_flutter_web
sha256: ff39211bd25d7fad125d19f757eba85bd154460907cd4d135e07e3d0f98a4130
url: "https://pub.dev"
source: hosted
version: "0.5.10"
graphs:
dependency: transitive
description:
@ -698,14 +714,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.9.0"
latlong2:
dependency: "direct main"
description:
name: latlong2
sha256: "98227922caf49e6056f91b6c56945ea1c7b166f28ffcd5fb8e72fc0b453cc8fe"
url: "https://pub.dev"
source: hosted
version: "0.9.1"
leak_tracker:
dependency: transitive
description:
@ -746,14 +754,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.0"
lists:
dependency: transitive
description:
name: lists
sha256: "4ca5c19ae4350de036a7e996cdd1ee39c93ac0a2b840f4915459b7d0a7d4ab27"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
logger:
dependency: "direct main"
description:
@ -778,14 +778,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.1.2-main.4"
maps_toolkit:
dependency: "direct main"
description:
name: maps_toolkit
sha256: "277877f9505208acacd2a0794ef190e836a5ffee58ebc8efc5b9ca8de50e3e2f"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
matcher:
dependency: transitive
description:
@ -810,14 +802,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.15.0"
mgrs_dart:
dependency: transitive
description:
name: mgrs_dart
sha256: fb89ae62f05fa0bb90f70c31fc870bcbcfd516c843fb554452ab3396f78586f7
url: "https://pub.dev"
source: hosted
version: "2.0.0"
mime:
dependency: transitive
description:
@ -870,18 +854,18 @@ packages:
dependency: transitive
description:
name: path_provider_android
sha256: c464428172cb986b758c6d1724c603097febb8fb855aa265aeecc9280c294d4a
sha256: "8c4967f8b7cb46dc914e178daa29813d83ae502e0529d7b0478330616a691ef7"
url: "https://pub.dev"
source: hosted
version: "2.2.12"
version: "2.2.14"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
version: "2.4.1"
path_provider_linux:
dependency: transitive
description:
@ -994,14 +978,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.8"
polylabel:
dependency: transitive
description:
name: polylabel
sha256: "41b9099afb2aa6c1730bdd8a0fab1400d287694ec7615dd8516935fa3144214b"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
pool:
dependency: transitive
description:
@ -1010,14 +986,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.5.1"
proj4dart:
dependency: transitive
description:
name: proj4dart
sha256: c8a659ac9b6864aa47c171e78d41bbe6f5e1d7bd790a5814249e6b68bc44324e
url: "https://pub.dev"
source: hosted
version: "2.1.0"
provider:
dependency: "direct main"
description:
@ -1050,6 +1018,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.28.0"
sanitize_html:
dependency: transitive
description:
name: sanitize_html
sha256: "12669c4a913688a26555323fb9cec373d8f9fbe091f2d01c40c723b33caa8989"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
screen_breakpoints:
dependency: "direct main"
description:
@ -1070,10 +1046,10 @@ packages:
dependency: transitive
description:
name: shared_preferences_android
sha256: "3b9febd815c9ca29c9e3520d50ec32f49157711e143b7a4ca039eb87e8ade5ab"
sha256: "7f172d1b06de5da47b6264c2692ee2ead20bbbc246690427cdb4fc301cd0c549"
url: "https://pub.dev"
source: hosted
version: "2.3.3"
version: "2.3.4"
shared_preferences_foundation:
dependency: transitive
description:
@ -1251,34 +1227,42 @@ packages:
dependency: "direct main"
description:
name: syncfusion_flutter_calendar
sha256: "0f049bbc7ea1f86a22db7c9090d967c71dc1c72f36b83393c66090141c37f819"
sha256: "20118a598cf5ae5b7ec84fa364e2b51070800f078466158fcd25864e1b5a2cfd"
url: "https://pub.dev"
source: hosted
version: "27.2.3"
version: "27.2.5"
syncfusion_flutter_core:
dependency: transitive
description:
name: syncfusion_flutter_core
sha256: a39ddfb22b30c7cba620fec7dc682e46f151998febd25bca5519c17431084951
sha256: "325f519ce4ad8edd81811c21b853d72018529e353584490824da0555156ba076"
url: "https://pub.dev"
source: hosted
version: "27.2.3"
version: "27.2.5"
syncfusion_flutter_datagrid:
dependency: "direct main"
description:
name: syncfusion_flutter_datagrid
sha256: "1ba098d01468ed9dd400bf69403eedfbbd3626dce675005697a2698834f05fde"
url: "https://pub.dev"
source: hosted
version: "27.2.5"
syncfusion_flutter_datepicker:
dependency: "direct main"
description:
name: syncfusion_flutter_datepicker
sha256: "5af3301119607fe834ca0d222013102884e6644fc8324430a8ff56f73442e3d5"
sha256: "2177e49eb8a1c0fce7081e40f5613c986d00e5e63cbeb98a6012f65ca156bfc7"
url: "https://pub.dev"
source: hosted
version: "27.2.3"
version: "27.2.5"
syncfusion_localizations:
dependency: "direct main"
description:
name: syncfusion_localizations
sha256: d6123f30f100a3e5e1dc235c6195194f5b82ca51fa8ce94718812b8b05120b60
sha256: c08ffb408a7425b911538be8d607a00bac82cb3be8d24a1a6af7a531bd889db0
url: "https://pub.dev"
source: hosted
version: "27.2.3"
version: "27.2.5"
synchronized:
dependency: transitive
description:
@ -1319,6 +1303,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.1"
toggle_switch:
dependency: "direct main"
description:
name: toggle_switch
sha256: dca04512d7c23ed320d6c5ede1211a404f177d54d353bf785b07d15546a86ce5
url: "https://pub.dev"
source: hosted
version: "2.3.0"
toml:
dependency: transitive
description:
@ -1335,14 +1327,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
unicode:
dependency: transitive
description:
name: unicode
sha256: "0f69e46593d65245774d4f17125c6084d2c20b4e473a983f6e21b7d7762218f1"
url: "https://pub.dev"
source: hosted
version: "0.3.1"
universal_html:
dependency: transitive
description:
@ -1415,14 +1399,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.1"
wkt_parser:
dependency: transitive
description:
name: wkt_parser
sha256: "8a555fc60de3116c00aad67891bcab20f81a958e4219cc106e3c037aa3937f13"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
xdg_directories:
dependency: transitive
description:

View file

@ -13,11 +13,6 @@ dependencies:
sdk: flutter
cupertino_icons: ^1.0.6
intl: ^0.19.0
flutter_map: ^7.0.2
latlong2: ^0.9.1
flutter_map_location_marker: ^9.1.1
flutter_map_cache: ^1.5.1
maps_toolkit: ^3.0.0
fast_immutable_collections: ^10.2.4
badges: ^3.1.2
logger: ^2.4.0
@ -62,6 +57,11 @@ dependencies:
bulleted_list: ^0.0.1+0.1a
super_bullet_list: ^0.0.3
animated_segmented_tab_control: ^2.0.0
google_maps_flutter: ^2.10.0
google_geocoding_api: ^1.5.2
toggle_switch: ^2.3.0
syncfusion_flutter_datagrid: ^27.2.5
dev_dependencies:

View file

@ -1,19 +1,6 @@
<!DOCTYPE html>
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8">
@ -96,6 +83,9 @@
100%{transform:rotate(330deg);}
}
</style>
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAb2d7gn5CLWnVZTaSapRYHjnZapSP9BQM&libraries=drawing"></script>
</head>
<body>
<div class="loader"></div>