Merge branch 'main' of bitbucket.org:venbainformationtechnology/ts-tat into main

This commit is contained in:
VE10-Sanjeev 2025-05-28 13:21:52 +00:00
commit bc00123e56
10 changed files with 863 additions and 301 deletions

View File

@ -1,8 +1,19 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="frontend"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true" >
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
<activity
android:name=".MainActivity"
android:exported="true"
@ -30,6 +41,8 @@
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and

View File

@ -5,6 +5,7 @@
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
@ -15,4 +16,7 @@
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
<paths>
<cache-path name="cache" path="." />
</paths>
</resources>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path name="cache" path="." />
<external-path name="external" path="." />
<!-- You can add more paths as needed -->
</paths>

View File

@ -98,6 +98,41 @@ class StatusDashboardState extends State<StatusDashboard> {
}
}
Color getStatusColor(String status) {
if (status == "Domestic") return Colors.white;
if (status == "International") return Colors.white;
if (status == "Partially Approved") return Colors.yellow.shade100;
if (status == "Pending Approval") return Colors.greenAccent.shade100;
if (status == "Approved") return Colors.green.shade100;
if (status == "Completed") return Colors.green.shade500;
if (status == "Rejected") return Colors.red.shade100;
if (status == "Cancelled") return Colors.red.shade200;
return Colors.grey.shade100;
}
IconData getStatusIcon(String title) {
switch (title) {
case "Domestic":
return Icons.directions_bus_filled_outlined;
case "International":
return Icons.airplanemode_active;
case "Partially Approved":
return Icons.pending;
case "Approved":
return Icons.check_circle_outline;
case "Completed":
return Icons.done_all;
case "Rejected":
return Icons.cancel_outlined;
case "Cancelled":
return Icons.cancel_schedule_send_outlined;
default:
return Icons.info_outline;
}
}
@override
Widget build(BuildContext context) {
final typeBasedCount = List<Map<String, dynamic>>.from(
@ -111,9 +146,12 @@ class StatusDashboardState extends State<StatusDashboard> {
print("statusBasedCount - => $statusBasedCount");
// 👇 Local function to create the card widget
Widget buildInfoCard(String title, int count, double width) {
print('title $title');
final color = getStatusColor(title);
final icon = getStatusIcon(title);
return Card(
elevation: 3,
color: Colors.white,
color: color,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Container(
width: width,
@ -121,10 +159,15 @@ class StatusDashboardState extends State<StatusDashboard> {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, size: 32, color: (title == 'Domestic' || title == 'International') ? Colors.green : Colors.black54), // 👈 Add Icon here
const SizedBox(height: 8),
Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(fontWeight: FontWeight.w600),
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: (title == 'Domestic' || title == 'International') ? 16 : 14,
),
),
const SizedBox(height: 8),
Text(
@ -132,7 +175,7 @@ class StatusDashboardState extends State<StatusDashboard> {
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.blue,
// color: Colors.blue,
),
),
],
@ -154,54 +197,63 @@ class StatusDashboardState extends State<StatusDashboard> {
body: Padding(
padding: isDesktop
? EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width * 0.1,
vertical: 0,
horizontal: MediaQuery.of(context).size.width *
0.1, // 30% of screen width as horizontal padding
vertical: 10, // 5% of screen height as vertical padding
)
: const EdgeInsets.all(0),
child: Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Wrap(
spacing: 10,
runSpacing: 10,
children: typeBasedCount.map((item) {
double cardWidth = isDesktop
? (MediaQuery.of(context).size.width * 0.75 - 10) / 2 // 80% width padding adjusted
: MediaQuery.of(context).size.width - 24; // full width with padding
: EdgeInsets.all(0),
child:Container(
// padding: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: isDesktop ? Colors.white : const Color(0xFFFCFCFC),
borderRadius: BorderRadius.circular(12), // 👈 Set your desired radius
),
// color: isDesktop ? Colors.white : Color(0xFFFCFCFC),
child: Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(30.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Wrap(
spacing: 10,
runSpacing: 10,
children: typeBasedCount.map((item) {
double cardWidth = isDesktop
? (MediaQuery.of(context).size.width * 0.75 - 10) / 2 // 80% width padding adjusted
: MediaQuery.of(context).size.width - 24; // full width with padding
return SizedBox(
width: cardWidth,
child: buildInfoCard(item['value'], item['count'], cardWidth),
);
}).toList(),
),
const SizedBox(height: 20),
Wrap(
spacing: 10,
runSpacing: 10,
children: statusBasedCount.map((item) {
double cardWidth = isDesktop
? (MediaQuery.of(context).size.width * 0.90 - 50) / 6 // desktop layout: 6 cards per row
: MediaQuery.of(context).size.width - 24; // mobile: full width
return SizedBox(
width: cardWidth,
child: buildInfoCard(item['value'], item['count'], cardWidth),
);
}).toList(),
),
const SizedBox(height: 20),
Wrap(
spacing: 10,
runSpacing: 10,
children: statusBasedCount.map((item) {
double cardWidth = isDesktop
? (MediaQuery.of(context).size.width * 0.90 - 50) / 6 // desktop layout: 6 cards per row
: MediaQuery.of(context).size.width - 24; // mobile: full width
return SizedBox(
width: cardWidth,
child: buildInfoCard(item['value'], item['count'], cardWidth),
);
}).toList(),
),
return SizedBox(
width: cardWidth,
child: buildInfoCard(item['value'], item['count'], cardWidth),
);
}).toList(),
),
],
),
),
),
],
),
],
),
),
),
],
),
)
),
);
});

View File

@ -0,0 +1,244 @@
import 'dart:convert';
import 'dart:io' as io show Directory, File;
import 'package:flutter_quill/flutter_quill.dart' hide Text;
import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart' hide Text;
import 'package:flutter_quill_extensions/flutter_quill_extensions.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_quill/flutter_quill_internal.dart';
import 'package:flutter_quill/quill_delta.dart';
import 'package:flutter_quill_extensions/flutter_quill_extensions.dart';
import 'package:frontend/Screens/myTemplates/quill_delta_sample.dart';
import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:path/path.dart' as path;
import 'package:responsive_builder/responsive_builder.dart';
import '../../routes/custom_appBar.dart';
import '../../routes/custom_drawer.dart';
import '../../services/apiService.dart';
import '../../utils/auth_utils.dart';
import '../../widgets/custom_user_travel.dart';
class Template extends StatefulWidget {
final Map<String, dynamic>? templateData;
const Template({super.key, required this.templateData});
static Template fromState(GoRouterState state) {
return Template(templateData: state.extra as Map<String, dynamic>?);
}
@override
TemplateState createState() => TemplateState();
}
class TemplateState extends State<Template> {
final ApiService apiService = ApiService();
// final QuillController _controller = QuillController.basic();
Color layoutColor = Colors.redAccent;
Color bodyColor = Colors.white;
final Map<String, TextEditingController> controllers = {};
List<String> dataHeader = ["subject"];
Map<String, dynamic> get TemplateData {
final data = {
// "org_id": orgId;
"template_name": controllers["templateName"]?.text,
"subject": controllers["subject"]?.text,
"body_html": controllers["bodyData"]?.text,
"placeholder": [],
// "created_by": userId
};
// Only add group_id if it's an edit operation
// if (widget.templateData != null && widget.templateData!.containsKey('group_id')) {
// data["template_id"] = templateData;
// }
return data;
}
@override
void initState() {
super.initState();
for (var field in dataHeader) {
controllers[field] = TextEditingController();
}
updateData();
loadInitialData();
}
@override
// void dispose() {
// // controllers.dispose();
// // _editorScrollController.dispose();
// _editorFocusNode.dispose();
// super.dispose();
// }
void loadInitialData() async {
String? layoutString = await getLayoutColor();
String? bodyStringColor = await getBodyColor();
setState(() {
layoutColor = layoutString != null
? Color(int.parse(layoutString))
: Colors.redAccent;
bodyColor = bodyStringColor != null
? Color(int.parse(bodyStringColor))
: Colors.white;
});
}
Future<void> updateData() async {
// Ensure apiselectedUser is not null before printing
if (widget.templateData != null) {
print("API Selected User Has Data - ${widget.templateData}");
print(
"API Selected User Has - ${widget.templateData?["templateData"]?["subject"]}");
setState(() {
// ✅ Wrap in setState to update the UI
controllers["subject"]?.text =
widget.templateData?["templateData"]?["subject"] ?? "";
// if (widget.group?["international_policy_id"] != null) {
// selectedInternational =
// widget.group!["international_policy_id"].toString();
// }
});
} else {
print("API Selected User Has Data - No data available yet");
}
}
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Scaffold(
backgroundColor: const Color(0xFFf5f5f5),
appBar: CustomAppBar(isDesktop: isDesktop),
drawer: CustomDrawer(isDesktop: false),
body: Padding(
padding: isDesktop
? EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width *
0.1, // 30% of screen width as horizontal padding
vertical: MediaQuery.of(context).size.height *
0, // 5% of screen height as vertical padding
)
: EdgeInsets.all(0),
child: Row(
children: [
// if (isDesktop) CustomDrawer(isDesktop: true),
Expanded(
child: buildUserTable(
isDesktop, context, bodyColor, layoutColor)),
],
),
),
);
});
}
Widget buildUserTable(
bool isDesktop, context, Color? bodyColor, Color layoutColor) {
return Container(
margin: isDesktop ? const EdgeInsets.only(top: 10.0, bottom: 10.0) : null,
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: isDesktop ? Colors.white : Color(0xFFFCFCFC),
),
child: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text("Editor"),
SizedBox(
height: 20,
),
buildTempalteSubject(isDesktop),
IconButton(
icon: const Icon(Icons.output),
tooltip: 'Print Delta JSON to log',
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text(
'The JSON Delta has been printed to the console.')));
},
),
SizedBox(
height: 20,
),
buildTempalteBody(isDesktop)
],
),
),
);
}
Widget buildTempalteSubject(bool isDesktop) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Subject",
style: GoogleFonts.poppins(
fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
isFocused: false,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
controller: controllers["subject"],
onChanged: (value) {
// _clearError("local_id_num");
},
decoration: InputDecoration(
labelText: "enter the subject",
labelStyle:
GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
);
}
Widget buildTempalteBody(bool isDesktop) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Content",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w400,
color: Colors.black,
),
),
const SizedBox(height: 10),
],
);
}
}

482
lib/Screens/myTemplates/tem Normal file
View File

@ -0,0 +1,482 @@
import 'dart:convert';
import 'dart:io' as io show Directory, File;
import 'package:flutter_quill/flutter_quill.dart' hide Text;
import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart' hide Text;
import 'package:flutter_quill_extensions/flutter_quill_extensions.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_quill/flutter_quill_internal.dart';
import 'package:flutter_quill/quill_delta.dart';
import 'package:flutter_quill_extensions/flutter_quill_extensions.dart';
import 'package:frontend/Screens/myTemplates/quill_delta_sample.dart';
import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:path/path.dart' as path;
import 'package:responsive_builder/responsive_builder.dart';
import '../../routes/custom_appBar.dart';
import '../../routes/custom_drawer.dart';
import '../../services/apiService.dart';
import '../../utils/auth_utils.dart';
import '../../widgets/custom_user_travel.dart';
class Template extends StatefulWidget {
final Map<String, dynamic>? templateData;
const Template({super.key, required this.templateData});
static Template fromState(GoRouterState state) {
return Template(templateData: state.extra as Map<String, dynamic>?);
}
@override
TemplateState createState() => TemplateState();
}
class TemplateState extends State<Template> {
final ApiService apiService = ApiService();
final QuillController _controller = () {
return QuillController.basic(
config: QuillControllerConfig(
clipboardConfig: QuillClipboardConfig(
enableExternalRichPaste: true,
onImagePaste: (imageBytes) async {
if (kIsWeb) {
// Dart IO is unsupported on the web.
return null;
}
// Save the image somewhere and return the image URL that will be
// stored in the Quill Delta JSON (the document).
final newFileName =
'image-file-${DateTime.now().toIso8601String()}.png';
final newPath = path.join(
io.Directory.systemTemp.path,
newFileName,
);
final file = await io.File(
newPath,
).writeAsBytes(imageBytes, flush: true);
return file.path;
},
),
));
}();
final FocusNode _editorFocusNode = FocusNode();
final ScrollController _editorScrollController = ScrollController();
// final QuillController _controller = QuillController.basic();
Color layoutColor = Colors.redAccent;
Color bodyColor = Colors.white;
final Map<String, TextEditingController> controllers = {};
List<String> dataHeader = ["subject"];
Map<String, dynamic> get TemplateData {
final data = {
// "org_id": orgId;
"template_name": controllers["templateName"]?.text,
"subject": controllers["subject"]?.text,
"body_html": controllers["bodyData"]?.text,
"placeholder": [],
// "created_by": userId
};
// Only add group_id if it's an edit operation
// if (widget.templateData != null && widget.templateData!.containsKey('group_id')) {
// data["template_id"] = templateData;
// }
return data;
}
@override
void initState() {
super.initState();
for (var field in dataHeader) {
controllers[field] = TextEditingController();
}
WidgetsBinding.instance.addPostFrameCallback((_) {
_editorFocusNode.requestFocus(); // ✅ Ensure focus is requested
});
_controller.document = Document.fromJson(kQuillDefaultSample);
// _controller.document = Document.fromJson(kQuillDefaultSample);
// _controller.document = Document.fromJson();
// _controller.document.toPlainText();
// _controller.readOnly = false;
updateData();
loadInitialData();
}
@override
// void dispose() {
// // controllers.dispose();
// // _editorScrollController.dispose();
// _editorFocusNode.dispose();
// super.dispose();
// }
void dispose() {
_controller.dispose();
_editorScrollController.dispose();
_editorFocusNode.dispose();
super.dispose();
}
void loadInitialData() async {
String? layoutString = await getLayoutColor();
String? bodyStringColor = await getBodyColor();
setState(() {
layoutColor = layoutString != null
? Color(int.parse(layoutString))
: Colors.redAccent;
bodyColor = bodyStringColor != null
? Color(int.parse(bodyStringColor))
: Colors.white;
});
}
Future<void> updateData() async {
// Ensure apiselectedUser is not null before printing
if (widget.templateData != null) {
print("API Selected User Has Data - ${widget.templateData}");
print(
"API Selected User Has - ${widget.templateData?["templateData"]?["subject"]}");
setState(() {
// ✅ Wrap in setState to update the UI
controllers["subject"]?.text =
widget.templateData?["templateData"]?["subject"] ?? "";
// if (widget.group?["international_policy_id"] != null) {
// selectedInternational =
// widget.group!["international_policy_id"].toString();
// }
});
} else {
print("API Selected User Has Data - No data available yet");
}
}
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Scaffold(
backgroundColor: const Color(0xFFf5f5f5),
appBar: CustomAppBar(isDesktop: isDesktop),
drawer: CustomDrawer(isDesktop: false),
body: Padding(
padding: isDesktop
? EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width *
0.1, // 30% of screen width as horizontal padding
vertical: MediaQuery.of(context).size.height *
0, // 5% of screen height as vertical padding
)
: EdgeInsets.all(0),
child: Row(
children: [
// if (isDesktop) CustomDrawer(isDesktop: true),
Expanded(
child: buildUserTable(
isDesktop, context, bodyColor, layoutColor)),
],
),
),
);
});
}
Widget buildUserTable(
bool isDesktop, context, Color? bodyColor, Color layoutColor) {
return Container(
margin: isDesktop ? const EdgeInsets.only(top: 10.0, bottom: 10.0) : null,
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: isDesktop ? Colors.white : Color(0xFFFCFCFC),
),
child: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text("Editor"),
SizedBox(
height: 20,
),
buildTempalteSubject(isDesktop),
IconButton(
icon: const Icon(Icons.output),
tooltip: 'Print Delta JSON to log',
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text(
'The JSON Delta has been printed to the console.')));
debugPrint(jsonEncode(_controller.document.toDelta().toJson()));
},
),
SizedBox(
height: 20,
),
buildTempalteBody(isDesktop)
],
),
),
);
}
Widget buildTempalteSubject(bool isDesktop) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Subject",
style: GoogleFonts.poppins(
fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
isFocused: false,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
controller: controllers["subject"],
onChanged: (value) {
// _clearError("local_id_num");
},
decoration: InputDecoration(
labelText: "enter the subject",
labelStyle:
GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
);
}
Widget buildTempalteBody(bool isDesktop) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Content",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w400,
color: Colors.black,
),
),
const SizedBox(height: 10),
// Expanded(
//
// child: QuillEditor(
// focusNode: _editorFocusNode,
// scrollController: _editorScrollController,
// controller: _controller,
// config: QuillEditorConfig(
// placeholder: 'Start writing your notes...',
// padding: const EdgeInsets.all(16),
// embedBuilders: [
// ...FlutterQuillEmbeds.editorBuilders(
// imageEmbedConfig: QuillEditorImageEmbedConfig(
// imageProviderBuilder: (context, imageUrl) {
// // https://pub.dev/packages/flutter_quill_extensions#-image-assets
// if (imageUrl.startsWith('assets/')) {
// return AssetImage(imageUrl);
// }
// return null;
// },
// ),
// videoEmbedConfig: QuillEditorVideoEmbedConfig(
// customVideoBuilder: (videoUrl, readOnly) {
// // To load YouTube videos https://github.com/singerdmx/flutter-quill/releases/tag/v10.8.0
// return null;
// },
// ),
// ),
// TimeStampEmbedBuilder(),
// ],
// ),
// ),
// ),
// ✅ Modern toolbar
QuillSimpleToolbar(
controller: _controller,
config: QuillSimpleToolbarConfig(
embedButtons: FlutterQuillEmbeds.toolbarButtons(),
showClipboardPaste: true,
customButtons: [
QuillToolbarCustomButtonOptions(
icon: const Icon(Icons.add_alarm_rounded),
onPressed: () {
_controller.document.insert(
_controller.selection.extentOffset,
TimeStampEmbed(
DateTime.now().toString(),
),
);
_controller.readOnly = true;
_controller.updateSelection(
TextSelection.collapsed(
offset: _controller.selection.extentOffset + 1,
),
ChangeSource.local,
);
},
),
],
buttonOptions: QuillSimpleToolbarButtonOptions(
base: QuillToolbarBaseButtonOptions(
afterButtonPressed: () {
final isDesktop = {
TargetPlatform.linux,
TargetPlatform.windows,
TargetPlatform.macOS
}.contains(defaultTargetPlatform);
if (isDesktop) {
_editorFocusNode.requestFocus();
}
},
),
linkStyle: QuillToolbarLinkStyleButtonOptions(
validateLink: (link) {
// Treats all links as valid. When launching the URL,
// `https://` is prefixed if the link is incomplete (e.g., `google.com` → `https://google.com`)
// however this happens only within the editor.
return true;
},
),
),
),
),
const SizedBox(height: 10),
// // ✅ Modern editor
Container(
height: 200,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
child: QuillEditor(
focusNode: _editorFocusNode,
scrollController: _editorScrollController,
controller: _controller,
config: QuillEditorConfig(
placeholder: 'Start writing your notes...',
padding: const EdgeInsets.all(16),
embedBuilders: [
...FlutterQuillEmbeds.editorBuilders(
imageEmbedConfig: QuillEditorImageEmbedConfig(
imageProviderBuilder: (context, imageUrl) {
// https://pub.dev/packages/flutter_quill_extensions#-image-assets
if (imageUrl.startsWith('assets/')) {
return AssetImage(imageUrl);
}
return null;
},
),
videoEmbedConfig: QuillEditorVideoEmbedConfig(
customVideoBuilder: (videoUrl, readOnly) {
// To load YouTube videos https://github.com/singerdmx/flutter-quill/releases/tag/v10.8.0
return null;
},
),
),
TimeStampEmbedBuilder(),
],
),
),
),
// child: QuillEditor.basic(
// focusNode: _editorFocusNode,
// scrollController: _editorScrollController,
// controller: _controller,
// // readOnly: true,
// config: QuillEditorConfig(
// requestKeyboardFocusOnCheckListChanged: false,
// // readOnlyMouseCursor: SystemMouseCursors.text,
// enableScribble: true,
// // readOnly: false,
// padding: const EdgeInsets.all(8),
// placeholder: 'Type something...',
// embedBuilders: [
// ...FlutterQuillEmbeds.editorBuilders(
// imageEmbedConfig: QuillEditorImageEmbedConfig(
// imageProviderBuilder: (context, imageUrl) {
// // https://pub.dev/packages/flutter_quill_extensions#-image-assets
// if (imageUrl.startsWith('assets/')) {
// return AssetImage(imageUrl);
// }
// return null;
// },
// ),
// videoEmbedConfig: QuillEditorVideoEmbedConfig(
// customVideoBuilder: (videoUrl, readOnly) {
// // To load YouTube videos https://github.com/singerdmx/flutter-quill/releases/tag/v10.8.0
// return null;
// },
// ),
// ),
// TimeStampEmbedBuilder(),
// ],
// ),
// ),
],
);
}
}
class TimeStampEmbed extends Embeddable {
const TimeStampEmbed(
String value,
) : super(timeStampType, value);
static const String timeStampType = 'timeStamp';
static TimeStampEmbed fromDocument(Document document) =>
TimeStampEmbed(jsonEncode(document.toDelta().toJson()));
Document get document => Document.fromJson(jsonDecode(data));
}
class TimeStampEmbedBuilder extends EmbedBuilder {
@override
String get key => 'timeStamp';
@override
String toPlainText(Embed node) {
return node.value.data;
}
@override
Widget build(
BuildContext context,
EmbedContext embedContext,
) {
return Row(
children: [
const Icon(Icons.access_time_rounded),
Text(embedContext.node.value.data as String),
],
);
}
}

View File

@ -39,34 +39,7 @@ class Template extends StatefulWidget {
class TemplateState extends State<Template> {
final ApiService apiService = ApiService();
final QuillController _controller = () {
return QuillController.basic(
config: QuillControllerConfig(
clipboardConfig: QuillClipboardConfig(
enableExternalRichPaste: true,
onImagePaste: (imageBytes) async {
if (kIsWeb) {
// Dart IO is unsupported on the web.
return null;
}
// Save the image somewhere and return the image URL that will be
// stored in the Quill Delta JSON (the document).
final newFileName =
'image-file-${DateTime.now().toIso8601String()}.png';
final newPath = path.join(
io.Directory.systemTemp.path,
newFileName,
);
final file = await io.File(
newPath,
).writeAsBytes(imageBytes, flush: true);
return file.path;
},
),
));
}();
final FocusNode _editorFocusNode = FocusNode();
final ScrollController _editorScrollController = ScrollController();
// final QuillController _controller = QuillController.basic();
Color layoutColor = Colors.redAccent;
Color bodyColor = Colors.white;
@ -99,14 +72,6 @@ class TemplateState extends State<Template> {
controllers[field] = TextEditingController();
}
WidgetsBinding.instance.addPostFrameCallback((_) {
_editorFocusNode.requestFocus(); // Ensure focus is requested
});
_controller.document = Document.fromJson(kQuillDefaultSample);
// _controller.document = Document.fromJson(kQuillDefaultSample);
// _controller.document = Document.fromJson();
// _controller.document.toPlainText();
// _controller.readOnly = false;
updateData();
loadInitialData();
@ -120,13 +85,6 @@ class TemplateState extends State<Template> {
// super.dispose();
// }
void dispose() {
_controller.dispose();
_editorScrollController.dispose();
_editorFocusNode.dispose();
super.dispose();
}
void loadInitialData() async {
String? layoutString = await getLayoutColor();
String? bodyStringColor = await getBodyColor();
@ -219,7 +177,6 @@ class TemplateState extends State<Template> {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text(
'The JSON Delta has been printed to the console.')));
debugPrint(jsonEncode(_controller.document.toDelta().toJson()));
},
),
SizedBox(
@ -280,202 +237,7 @@ class TemplateState extends State<Template> {
color: Colors.black,
),
),
const SizedBox(height: 10),
// Expanded(
//
// child: QuillEditor(
// focusNode: _editorFocusNode,
// scrollController: _editorScrollController,
// controller: _controller,
// config: QuillEditorConfig(
// placeholder: 'Start writing your notes...',
// padding: const EdgeInsets.all(16),
// embedBuilders: [
// ...FlutterQuillEmbeds.editorBuilders(
// imageEmbedConfig: QuillEditorImageEmbedConfig(
// imageProviderBuilder: (context, imageUrl) {
// // https://pub.dev/packages/flutter_quill_extensions#-image-assets
// if (imageUrl.startsWith('assets/')) {
// return AssetImage(imageUrl);
// }
// return null;
// },
// ),
// videoEmbedConfig: QuillEditorVideoEmbedConfig(
// customVideoBuilder: (videoUrl, readOnly) {
// // To load YouTube videos https://github.com/singerdmx/flutter-quill/releases/tag/v10.8.0
// return null;
// },
// ),
// ),
// TimeStampEmbedBuilder(),
// ],
// ),
// ),
// ),
// Modern toolbar
QuillSimpleToolbar(
controller: _controller,
config: QuillSimpleToolbarConfig(
embedButtons: FlutterQuillEmbeds.toolbarButtons(),
showClipboardPaste: true,
customButtons: [
QuillToolbarCustomButtonOptions(
icon: const Icon(Icons.add_alarm_rounded),
onPressed: () {
_controller.document.insert(
_controller.selection.extentOffset,
TimeStampEmbed(
DateTime.now().toString(),
),
);
_controller.readOnly = true;
_controller.updateSelection(
TextSelection.collapsed(
offset: _controller.selection.extentOffset + 1,
),
ChangeSource.local,
);
},
),
],
buttonOptions: QuillSimpleToolbarButtonOptions(
base: QuillToolbarBaseButtonOptions(
afterButtonPressed: () {
final isDesktop = {
TargetPlatform.linux,
TargetPlatform.windows,
TargetPlatform.macOS
}.contains(defaultTargetPlatform);
if (isDesktop) {
_editorFocusNode.requestFocus();
}
},
),
linkStyle: QuillToolbarLinkStyleButtonOptions(
validateLink: (link) {
// Treats all links as valid. When launching the URL,
// `https://` is prefixed if the link is incomplete (e.g., `google.com` `https://google.com`)
// however this happens only within the editor.
return true;
},
),
),
),
),
const SizedBox(height: 10),
// // Modern editor
Container(
height: 200,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
child: QuillEditor(
focusNode: _editorFocusNode,
scrollController: _editorScrollController,
controller: _controller,
config: QuillEditorConfig(
placeholder: 'Start writing your notes...',
padding: const EdgeInsets.all(16),
embedBuilders: [
...FlutterQuillEmbeds.editorBuilders(
imageEmbedConfig: QuillEditorImageEmbedConfig(
imageProviderBuilder: (context, imageUrl) {
// https://pub.dev/packages/flutter_quill_extensions#-image-assets
if (imageUrl.startsWith('assets/')) {
return AssetImage(imageUrl);
}
return null;
},
),
videoEmbedConfig: QuillEditorVideoEmbedConfig(
customVideoBuilder: (videoUrl, readOnly) {
// To load YouTube videos https://github.com/singerdmx/flutter-quill/releases/tag/v10.8.0
return null;
},
),
),
TimeStampEmbedBuilder(),
],
),
),
),
// child: QuillEditor.basic(
// focusNode: _editorFocusNode,
// scrollController: _editorScrollController,
// controller: _controller,
// // readOnly: true,
// config: QuillEditorConfig(
// requestKeyboardFocusOnCheckListChanged: false,
// // readOnlyMouseCursor: SystemMouseCursors.text,
// enableScribble: true,
// // readOnly: false,
// padding: const EdgeInsets.all(8),
// placeholder: 'Type something...',
// embedBuilders: [
// ...FlutterQuillEmbeds.editorBuilders(
// imageEmbedConfig: QuillEditorImageEmbedConfig(
// imageProviderBuilder: (context, imageUrl) {
// // https://pub.dev/packages/flutter_quill_extensions#-image-assets
// if (imageUrl.startsWith('assets/')) {
// return AssetImage(imageUrl);
// }
// return null;
// },
// ),
// videoEmbedConfig: QuillEditorVideoEmbedConfig(
// customVideoBuilder: (videoUrl, readOnly) {
// // To load YouTube videos https://github.com/singerdmx/flutter-quill/releases/tag/v10.8.0
// return null;
// },
// ),
// ),
// TimeStampEmbedBuilder(),
// ],
// ),
// ),
],
);
}
}
class TimeStampEmbed extends Embeddable {
const TimeStampEmbed(
String value,
) : super(timeStampType, value);
static const String timeStampType = 'timeStamp';
static TimeStampEmbed fromDocument(Document document) =>
TimeStampEmbed(jsonEncode(document.toDelta().toJson()));
Document get document => Document.fromJson(jsonDecode(data));
}
class TimeStampEmbedBuilder extends EmbedBuilder {
@override
String get key => 'timeStamp';
@override
String toPlainText(Embed node) {
return node.value.data;
}
@override
Widget build(
BuildContext context,
EmbedContext embedContext,
) {
return Row(
children: [
const Icon(Icons.access_time_rounded),
Text(embedContext.node.value.data as String),
],
);
}

View File

@ -780,7 +780,8 @@ class _PolicyState extends State<Policy> {
return Container(
margin: const EdgeInsets.only(top: 8.0),
// color: Colors.white,
height: isDesktop ? MediaQuery.of(context).size.height * 0.55 : null,
height: isDesktop ? 350 : null,
// height: isDesktop ? MediaQuery.of(context).size.height * 0.55 : null,
// color: Colors.blueGrey.shade200,
width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
decoration: BoxDecoration(
@ -966,11 +967,10 @@ class _PolicyState extends State<Policy> {
child: isDesktop
? Container(
// color: Colors.amber,
child: Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [_buildPolicyServiceOrdering(isDesktop)],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [_buildPolicyServiceOrdering(isDesktop)],
),
)
: Container(
@ -995,7 +995,7 @@ class _PolicyState extends State<Policy> {
List<String> services =
ServicesChoosed!.map((service) => service['name'].toString()).toList();
return Expanded(
return Container(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Flex(

View File

@ -270,6 +270,11 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.3.2"
flutter_quill:
git:
url: https://github.com/singerdmx/flutter-quill.git
ref: v11.4.1
flutter_localizations:
dependency: transitive
description: flutter
@ -283,14 +288,7 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.27"
flutter_quill:
dependency: "direct main"
description:
name: flutter_quill
sha256: "7e60963632bbc8615627f0bae8e178515f69ecb378ad49fa68c43c2aabf33e21"
url: "https://pub.dev"
source: hosted
version: "11.4.1"
flutter_quill_delta_from_html:
dependency: transitive
description:

View File

@ -54,6 +54,7 @@ dependencies:
flutter_quill_extensions: ^11.0.0
flutter_localization: ^0.3.2
reorderables: ^0.6.0
html_editor_enhanced: ^1.8.0
dev_dependencies: