Update: 2026-08-08 17:35:50

This commit is contained in:
Hamza-Ayed
2026-08-08 17:35:50 +03:00
parent 8c9f729ecd
commit e63f990aea
18 changed files with 261 additions and 75 deletions
+40 -1
View File
@@ -371,9 +371,48 @@ class SiroGeminiService {
return null; return null;
} }
$data = json_decode($response, true);
$text = $data['candidates'][0]['content']['parts'][0]['text'] ?? ''; $text = $data['candidates'][0]['content']['parts'][0]['text'] ?? '';
return trim(preg_replace('/```html|```/', '', $text)); return trim(preg_replace('/```html|```/', '', $text));
}
/**
* Answers a driver support inquiry acting as Siro's smart assistant.
*/
public function answerSupportInquiry(
string $question,
array $driverContext,
string $model = 'gemini-flash-lite-latest'
): ?string {
if (!$this->apiKey) return null;
$contextJson = json_encode($driverContext, JSON_UNESCAPED_UNICODE);
$prompt = "
أنت مساعد الدعم الفني الذكي لتطبيق 'سيرو' (Siro) لنقل الركاب.
وظيفتك مساعدة السائقين (الكباتن) والإجابة على استفساراتهم باحترافية، ودقة، وبلهجة ودية ومهذبة جداً.
بيانات السائق الحالية (سياق مهم للإجابة على سؤاله):
$contextJson
سؤال السائق:
\"$question\"
المطلوب منك:
1. الإجابة مباشرة على سؤال السائق بناءً على السياق المعطى إذا كان يخص رحلاته أو رصيده المذكور في السياق.
2. إذا سأل عن إجراءات عامة، أجب كخبير في سيرو.
3. اجعل الإجابة ودية وواضحة جداً وقصيرة.
4. الرد يكون باللغة العربية حصراً.
5. لا تذكر تفاصيل تقنية (مثل json أو database)، بل تحدث كإنسان يقدم الدعم.
";
$prompt .= "\n\nالرجاء إرجاع الإجابة بصيغة JSON فقط كالتالي:\n{\n \"reply\": \"نص الإجابة هنا\"\n}";
$response = $this->callGemini($prompt, $model);
if ($response && isset($response['reply'])) {
return $response['reply'];
}
return null; return null;
} }
}
} }
+109
View File
@@ -0,0 +1,109 @@
<?php
require_once __DIR__ . '/../../connect.php';
require_once __DIR__ . '/../../core/Services/SiroGeminiService.php';
require_once __DIR__ . '/../../encrypt_decrypt.php';
// Variables from connect.php (JWT token)
global $user_id, $role, $con;
if (!$user_id) {
jsonError("Unauthorized");
exit;
}
$driverID = $user_id;
$helpQuestion = filterRequest("helpQuestion");
if (empty($helpQuestion)) {
jsonError("Missing parameters");
exit;
}
try {
global $encryptionHelper;
// 1. Fetch driver info (Decrypting names and phone)
$stmt = $con->prepare("SELECT `first_name`, `last_name`, `phone` FROM `driver` WHERE `id` = :id");
$stmt->bindParam(':id', $driverID);
$stmt->execute();
$driverRow = $stmt->fetch(PDO::FETCH_ASSOC);
$driverInfo = [];
if ($driverRow) {
$driverInfo = [
'first_name' => $driverRow['first_name'] ? $encryptionHelper->decryptData($driverRow['first_name']) : '',
'last_name' => $driverRow['last_name'] ? $encryptionHelper->decryptData($driverRow['last_name']) : '',
'phone' => $driverRow['phone'] ? $encryptionHelper->decryptData($driverRow['phone']) : '',
'user_type' => $role
];
}
// 2. Fetch wallet balance using S2S
$walletServer = getenv('PAYMENT_SERVER_URL') ?: 'https://wallet.siromove.com';
$walletUrl = "$walletServer/v2/main/ride/driverWallet/get_s2s_wallet_dashboard.php";
$ch = curl_init($walletUrl);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query(["driverID" => $driverID]),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 5,
CURLOPT_HTTPHEADER => [
'Content-Type: application/x-www-form-urlencoded',
'X-S2S-Api-Key: ' . getenv('S2S_SHARED_KEY')
]
]);
$s2sRes = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$totalWallet = 0.0;
if ($httpCode === 200 && $s2sRes) {
$resDecoded = json_decode($s2sRes, true);
if ($resDecoded && isset($resDecoded['status']) && $resDecoded['status'] === 'success') {
$totalWallet = (float)($resDecoded['message']['totalWallet'] ?? 0.0);
}
}
$walletInfo = ['balance' => $totalWallet];
// 3. Fetch recent trips (Correct table is `ride` and correct columns)
$recentTrips = [];
try {
$stmt3 = $con->prepare("SELECT `id`, `date`, `time`, `status`, `price`, `paymentMethod` FROM `ride` WHERE `driver_id` = :id ORDER BY `id` DESC LIMIT 3");
$stmt3->bindParam(':id', $driverID);
$stmt3->execute();
$recentTrips = $stmt3->fetchAll(PDO::FETCH_ASSOC) ?: [];
} catch (PDOException $e) {
// Ignored
}
$contextData = [
'driver_info' => $driverInfo,
'wallet_balance' => $walletInfo,
'recent_trips' => $recentTrips,
];
// 4. Call Gemini
$geminiService = new SiroGeminiService();
$aiReply = $geminiService->answerSupportInquiry($helpQuestion, $contextData);
if (!$aiReply) {
$aiReply = "مرحباً كابتن، استلمنا استفسارك ولكن المساعد الذكي غير متاح حالياً. سيقوم فريق الدعم بمراجعة رسالتك قريباً.";
}
// 5. Insert into helpCenter
$sql = "INSERT INTO `helpCenter` (`driverID`, `helpQuestion`, `replay`) VALUES (:driverID, :helpQuestion, :replay)";
$stmt4 = $con->prepare($sql);
$stmt4->bindParam(':driverID', $driverID);
$stmt4->bindParam(':helpQuestion', $helpQuestion);
$stmt4->bindParam(':replay', $aiReply);
$stmt4->execute();
if ($stmt4->rowCount() > 0) {
jsonSuccess(null, "Help question saved and answered");
} else {
jsonError("Failed to save help question");
}
} catch (Exception $e) {
jsonError("An error occurred while processing your request.");
}
?>
+1 -1
View File
@@ -469,7 +469,7 @@ class AppLink {
static String get updateTips => "$ride/tips/update.php"; static String get updateTips => "$ride/tips/update.php";
//-----------------Help Center------------------ //-----------------Help Center------------------
static String get addhelpCenter => "$ride/helpCenter/add.php"; static String get addhelpCenter => "$ride/helpCenter/ask_gemini.php";
static String get gethelpCenter => "$ride/helpCenter/get.php"; static String get gethelpCenter => "$ride/helpCenter/get.php";
static String get getByIdhelpCenter => "$ride/helpCenter/getById.php"; static String get getByIdhelpCenter => "$ride/helpCenter/getById.php";
static String get updatehelpCenter => "$ride/helpCenter/update.php"; static String get updatehelpCenter => "$ride/helpCenter/update.php";
@@ -5,11 +5,8 @@ import 'package:get/get.dart';
import '../../../../constant/box_name.dart'; import '../../../../constant/box_name.dart';
import '../../../../constant/links.dart'; import '../../../../constant/links.dart';
import '../../../../constant/style.dart';
import '../../../../main.dart'; import '../../../../main.dart';
import '../../../../views/widgets/elevated_btn.dart';
import '../../../functions/crud.dart'; import '../../../functions/crud.dart';
import '../../../functions/encrypt_decrypt.dart';
class HelpController extends GetxController { class HelpController extends GetxController {
bool isLoading = false; bool isLoading = false;
@@ -20,6 +17,9 @@ class HelpController extends GetxController {
String status = ''; String status = '';
String qustion = ''; String qustion = '';
late int indexQuestion = 0; late int indexQuestion = 0;
Map<int, String> aiReplies = {};
getIndex(int i, String qustion1) async { getIndex(int i, String qustion1) async {
indexQuestion = i; indexQuestion = i;
qustion = qustion1; qustion = qustion1;
@@ -38,13 +38,15 @@ class HelpController extends GetxController {
update(); update();
if (d['status'].toString() == 'success') { if (d['status'].toString() == 'success') {
getHelpQuestion(); getHelpQuestion();
// Get.snackbar('Feedback data saved successfully'.tr, '', helpQuestionController.clear();
// backgroundColor: AppColor.greenColor,
// snackPosition: SnackPosition.BOTTOM);
} }
} }
void getHelpQuestion() async { void getHelpQuestion() async {
getHelpQuestionFuture();
}
Future<void> getHelpQuestionFuture() async {
isLoading = true; isLoading = true;
update(); update();
var res = await CRUD().get(link: AppLink.gethelpCenter, payload: { var res = await CRUD().get(link: AppLink.gethelpCenter, payload: {
@@ -52,27 +54,9 @@ class HelpController extends GetxController {
}); });
if (res == "failure") { if (res == "failure") {
isLoading = false; isLoading = false;
helpQuestionDate = {'message': []};
update(); update();
Get.defaultDialog( return;
title: 'There is no help Question here'.tr,
titleStyle: AppStyle.title,
middleText: '',
confirm: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
MyElevatedButton(
title: 'Add Question'.tr,
onPressed: () {
Get.back();
}),
MyElevatedButton(
title: 'Back'.tr,
onPressed: () {
Get.back();
Get.back();
}),
],
));
} }
helpQuestionDate = jsonDecode(res); helpQuestionDate = jsonDecode(res);
isLoading = false; isLoading = false;
@@ -89,14 +73,20 @@ class HelpController extends GetxController {
status = 'not yet'; status = 'not yet';
isLoading = false; isLoading = false;
update(); update();
} else {
status = 'done';
helpQuestionRepleyDate = jsonDecode(res);
isLoading = false;
update();
} }
helpQuestionRepleyDate = jsonDecode(res);
isLoading = false;
update();
} }
@override @override
void onInit() { void onInit() {
final storedReplies = box.read<Map<String, dynamic>>('ai_replies') ?? {};
aiReplies = storedReplies
.map((key, value) => MapEntry(int.parse(key), value.toString()));
getHelpQuestion(); getHelpQuestion();
super.onInit(); super.onInit();
} }
@@ -2717,4 +2717,52 @@ final Map<String, String> ar_jo = {
"My Earnings": "أرباحي", "My Earnings": "أرباحي",
"Upcoming Scheduled Rides": "الرحلات المجدولة القادمة", "Upcoming Scheduled Rides": "الرحلات المجدولة القادمة",
"Retry": "إعادة المحاولة", "Retry": "إعادة المحاولة",
// Insurance
"Cancelling stops future premiums. Outstanding dues remain payable.": "يؤدي الإلغاء إلى إيقاف الأقساط المستقبلية. تبقى المستحقات السابقة واجبة الدفع.",
"Insurance plans have not been launched in your area yet. Once a partner plan is available it will appear here automatically.": "لم يتم إطلاق خطط التأمين في منطقتك بعد. بمجرد توفر خطة الشريك، ستظهر هنا تلقائياً.",
"No insurance plans available yet": "لا توجد خطط تأمين متاحة بعد",
"Outstanding": "المستحق",
"Settled": "المدفوع",
"Cancel policy": "إلغاء الوثيقة",
"Subscribe": "اشتراك",
"Not eligible yet": "غير مؤهل بعد",
"Premium ledger": "سجل الأقساط",
"Insurance": "التأمين",
"Premium": "القسط",
"Started on": "بدأ في",
"Policy number": "رقم الوثيقة",
"rides": "رحلة",
"Refresh": "تحديث",
// Scheduled rides
"Could not load scheduled rides": "تعذر تحميل الرحلات المجدولة",
"Please check your connection and try again.": "يرجى التحقق من اتصالك والمحاولة مرة أخرى.",
"You have no upcoming scheduled rides": "ليس لديك أي رحلات مجدولة قادمة",
"Starting Point": "نقطة الانطلاق",
"Destination": "الوجهة",
"Currency": "دينار أردني",
// Invites
"Invite": "دعوة",
"Drivers": "سائقون",
"Passengers": "ركاب",
"Your Permanent Referral Code": "رمز الدعوة الدائم الخاص بك",
"Share via WhatsApp Groups": "شارك عبر مجموعات الواتساب",
"Enter Inviter's Code": "أدخل رمز الداعي",
"If someone referred you, enter their code below to link accounts and receive rewards.": "إذا قام شخص ما بدعوتك، أدخل رمزه أدناه لربط الحسابات والحصول على المكافآت.",
"Enter Code (e.g. AB1234)": "أدخل الرمز (مثال: AB1234)",
"Link": "ربط",
"Please enter a referral code": "يرجى إدخال رمز الدعوة",
"Enter phone": "أدخل رقم الهاتف",
"Send Invite": "إرسال دعوة",
"Show Invitations": "عرض الدعوات",
"Invitations Sent": "الدعوات المرسلة",
"No invitation found yet!": "لم يتم العثور على أي دعوات بعد!",
"Trip": "رحلة",
"Choose from contact": "اختر من جهات الاتصال",
"Invite another driver and both get a gift after he completes 100 trips!": "ادعُ سائقًا آخر واحصل كليكما على هدية بعد إكماله 100 رحلة!",
"Share this code with passengers and earn rewards when they use it!": "شارك هذا الرمز مع الركاب واكسب مكافآت عند استخدامه!",
// Help Center
"No questions asked yet.": "لم يتم طرح أي أسئلة بعد.",
}; };
@@ -0,0 +1 @@
// This file is no longer used. Gemini is now handled in the PHP backend.
@@ -7,6 +7,8 @@ import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:siro_driver/controller/auth/captin/history_captain.dart'; import 'package:siro_driver/controller/auth/captin/history_captain.dart';
import 'package:siro_driver/controller/functions/launch.dart'; import 'package:siro_driver/controller/functions/launch.dart';
import '../../../widgets/ui/siro_ui.dart';
class HistoryDetailsPage extends StatefulWidget { class HistoryDetailsPage extends StatefulWidget {
const HistoryDetailsPage({super.key}); const HistoryDetailsPage({super.key});
@@ -32,13 +34,8 @@ class _HistoryDetailsPageState extends State<HistoryDetailsPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return SiroPage(
backgroundColor: Colors.grey[50], title: 'Trip Details'.tr,
appBar: AppBar(
title: Text('Trip Details'.tr),
backgroundColor: Colors.white,
elevation: 1,
),
body: GetBuilder<HistoryCaptainController>( body: GetBuilder<HistoryCaptainController>(
builder: (controller) { builder: (controller) {
if (controller.isloading) { if (controller.isloading) {
@@ -256,12 +253,9 @@ class _DetailCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Card( return Padding(
elevation: 2, padding: const EdgeInsets.only(bottom: 16.0),
shadowColor: Colors.black.withValues(alpha: 0.05), child: SiroCard(
margin: const EdgeInsets.only(bottom: 16.0),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -440,7 +440,7 @@ class UserHeader extends StatelessWidget {
), ),
otherAccountsPictures: [ otherAccountsPictures: [
Container( Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.15), color: Colors.white.withValues(alpha: 0.15),
borderRadius: BorderRadius.circular(999), borderRadius: BorderRadius.circular(999),
@@ -88,6 +88,13 @@ class HelpDetailsReplayPage extends StatelessWidget {
replayData == null || replayData == null ||
EncryptionHelper.instance.decryptData(replayData['replay']).toString() == 'not yet'; EncryptionHelper.instance.decryptData(replayData['replay']).toString() == 'not yet';
if (isNoReply && helpController.aiReplies.containsKey(helpController.indexQuestion)) {
return Text(
helpController.aiReplies[helpController.indexQuestion]!,
style: Theme.of(context).textTheme.bodyLarge,
);
}
return Text( return Text(
isNoReply isNoReply
? 'No Response yet.'.tr ? 'No Response yet.'.tr
@@ -28,6 +28,7 @@ class DriverInsurancePage extends StatelessWidget {
onRefresh: c.refreshAll, onRefresh: c.refreshAll,
color: SiroUi.interactive(context), color: SiroUi.interactive(context),
child: ListView( child: ListView(
physics: const AlwaysScrollableScrollPhysics(),
padding: SiroUi.pageInsets, padding: SiroUi.pageInsets,
children: children:
c.hasPolicy ? _policyView(context, c) : _plansView(context, c), c.hasPolicy ? _policyView(context, c) : _plansView(context, c),
@@ -157,8 +158,6 @@ class DriverInsurancePage extends StatelessWidget {
message: message:
'Insurance plans have not been launched in your area yet. Once a partner plan is available it will appear here automatically.' 'Insurance plans have not been launched in your area yet. Once a partner plan is available it will appear here automatically.'
.tr, .tr,
actionLabel: 'Refresh'.tr,
onAction: c.isLoading.value ? null : () => c.refreshAll(),
), ),
]; ];
} }
@@ -39,6 +39,7 @@ class DriverScheduledRidesPage extends StatelessWidget {
onRefresh: controller.fetch, onRefresh: controller.fetch,
color: SiroUi.interactive(context), color: SiroUi.interactive(context),
child: ListView.builder( child: ListView.builder(
physics: const AlwaysScrollableScrollPhysics(),
padding: SiroUi.pageInsets, padding: SiroUi.pageInsets,
itemCount: controller.bookings.length, itemCount: controller.bookings.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
@@ -8,6 +8,7 @@ import '../../../constant/links.dart';
import '../../../constant/style.dart'; import '../../../constant/style.dart';
import '../../../controller/functions/crud.dart'; import '../../../controller/functions/crud.dart';
import '../../../main.dart'; import '../../../main.dart';
import '../../widgets/ui/siro_ui.dart';
// --- ملاحظات هامة --- // --- ملاحظات هامة ---
// 1. تأكد من إضافة الرابط الجديد إلى ملف AppLink الخاص بك: // 1. تأكد من إضافة الرابط الجديد إلى ملف AppLink الخاص بك:
@@ -162,8 +163,8 @@ class _EcashDriverPaymentScreenState extends State<EcashDriverPaymentScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return SiroPage(
appBar: AppBar(title: Text('Complete Payment'.tr)), title: 'Complete Payment'.tr,
body: WebViewWidget(controller: _controller), body: WebViewWidget(controller: _controller),
); );
} }
@@ -5,6 +5,7 @@ import 'package:get/get.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import '../../../constant/links.dart'; import '../../../constant/links.dart';
import '../../../controller/functions/crud.dart'; import '../../../controller/functions/crud.dart';
import '../../widgets/ui/siro_ui.dart';
class PaymentScreenCliq extends StatefulWidget { class PaymentScreenCliq extends StatefulWidget {
final double amount; final double amount;
@@ -89,9 +90,8 @@ class _PaymentScreenCliqState extends State<PaymentScreenCliq> with SingleTicker
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return SiroPage(
backgroundColor: Colors.grey[50], title: "Cliq Payment",
appBar: AppBar(title: const Text("Cliq Payment"), centerTitle: true, backgroundColor: Colors.white, foregroundColor: Colors.black),
body: SafeArea( body: SafeArea(
child: Padding( child: Padding(
padding: const EdgeInsets.all(20.0), padding: const EdgeInsets.all(20.0),
@@ -5,6 +5,7 @@ import 'package:get/get.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import '../../../constant/links.dart'; import '../../../constant/links.dart';
import '../../../controller/functions/crud.dart'; import '../../../controller/functions/crud.dart';
import '../../widgets/ui/siro_ui.dart';
class PaymentScreenMtn extends StatefulWidget { class PaymentScreenMtn extends StatefulWidget {
final double amount; final double amount;
@@ -89,9 +90,8 @@ class _PaymentScreenMtnState extends State<PaymentScreenMtn> with SingleTickerPr
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return SiroPage(
backgroundColor: Colors.grey[50], title: "MTN Payment",
appBar: AppBar(title: const Text("MTN Payment"), centerTitle: true, backgroundColor: Colors.white, foregroundColor: Colors.black),
body: SafeArea( body: SafeArea(
child: Padding( child: Padding(
padding: const EdgeInsets.all(20.0), padding: const EdgeInsets.all(20.0),
@@ -3,6 +3,7 @@ import 'package:get/get.dart';
import '../../../constant/finance_design_system.dart'; import '../../../constant/finance_design_system.dart';
import '../../../controller/home/captin/behavior_controller.dart'; import '../../../controller/home/captin/behavior_controller.dart';
import '../../widgets/ui/siro_ui.dart';
class BehaviorPage extends StatelessWidget { class BehaviorPage extends StatelessWidget {
const BehaviorPage({super.key}); const BehaviorPage({super.key});
@@ -12,23 +13,11 @@ class BehaviorPage extends StatelessWidget {
final controller = Get.put(DriverBehaviorController()); final controller = Get.put(DriverBehaviorController());
controller.fetchDriverBehavior(); controller.fetchDriverBehavior();
return Scaffold( return SiroPage(
backgroundColor: FinanceDesignSystem.backgroundColor, title: 'Driver Behavior'.tr,
appBar: AppBar(
title: Text('Driver Behavior'.tr,
style: TextStyle(
fontWeight: FontWeight.bold,
color: FinanceDesignSystem.primaryDark)),
centerTitle: true,
backgroundColor: FinanceDesignSystem.cardColor,
elevation: 0,
iconTheme: IconThemeData(color: FinanceDesignSystem.primaryDark),
),
body: Obx(() { body: Obx(() {
if (controller.isLoading.value) { if (controller.isLoading.value) {
return Center( return const SiroLoading();
child: CircularProgressIndicator(
color: FinanceDesignSystem.accentBlue));
} }
double score = controller.overallScore.value; double score = controller.overallScore.value;
@@ -31,8 +31,8 @@ class EarningsPage extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
// شريط العنوان يأتي من الثيم — كان يبني ألوانه يدوياً في كل صفحة، // شريط العنوان يأتي من الثيم — كان يبني ألوانه يدوياً في كل صفحة،
// فيختلف ارتفاعه ووزن خطه من شاشة لأخرى. // فيختلف ارتفاعه ووزن خطه من شاشة لأخرى.
return Scaffold( return SiroPage(
appBar: AppBar(title: Text('My Earnings'.tr)), title: 'My Earnings'.tr,
body: GetBuilder<EarningsController>( body: GetBuilder<EarningsController>(
builder: (ctl) { builder: (ctl) {
if (ctl.isLoading && ctl.summary == null) { if (ctl.isLoading && ctl.summary == null) {
@@ -18,9 +18,9 @@ class StatSummaryCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
// ‏رأسي 12 لا 16: البطاقة داخل شبكة بارتفاع ثابت، و16 كانت تتجاوزه // ‏رأسي 8 لا 12: البطاقة داخل شبكة بارتفاع ثابت، و12 كانت تتجاوزه
// ‏بنحو 10px مع خط الرقم مقاس 22. // ‏قليلاً مع خط الرقم مقاس 22.
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration( decoration: BoxDecoration(
color: FinanceDesignSystem.cardColor, color: FinanceDesignSystem.cardColor,
borderRadius: BorderRadius.circular(FinanceDesignSystem.cardRadius), borderRadius: BorderRadius.circular(FinanceDesignSystem.cardRadius),
+8
View File
@@ -1109,6 +1109,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.1.0" version: "8.1.0"
google_generative_ai:
dependency: "direct main"
description:
name: google_generative_ai
sha256: "71f613d0247968992ad87a0eb21650a566869757442ba55a31a81be6746e0d1f"
url: "https://pub.dev"
source: hosted
version: "0.4.7"
graphs: graphs:
dependency: transitive dependency: transitive
description: description: