FAQs
This commit is contained in:
parent
6de1ea4a49
commit
45fcefeb81
@ -1,151 +0,0 @@
|
||||
<!--<!DOCTYPE html>-->
|
||||
<!--<html lang="en">-->
|
||||
<!--<head>-->
|
||||
<!-- <meta charset="UTF-8" />-->
|
||||
<!-- <style>-->
|
||||
<!-- #botmanWidgetRoot svg {-->
|
||||
<!-- display: none !important;-->
|
||||
<!-- }-->
|
||||
<!-- </style>-->
|
||||
<!-- <script>-->
|
||||
<!-- const APIENDPOINT = 'https://venbait.in/nhance/dev';-->
|
||||
<!-- const APIAPIENDPOINT = 'https://venbait.in/nhance/dev';-->
|
||||
|
||||
<!-- var emp_name = 'Kumar';-->
|
||||
<!-- var uid = Math.floor(100000 + Math.random() * 900000);-->
|
||||
<!-- console.log('CURRENT_USER' + uid);-->
|
||||
|
||||
<!-- window.botmanWidget = {-->
|
||||
<!-- chatServer: APIENDPOINT + '/chat',-->
|
||||
<!-- frameEndpoint: APIAPIENDPOINT + '/widget',-->
|
||||
<!-- title: 'Ask ILA',-->
|
||||
<!-- introMessage: '',-->
|
||||
<!-- bubbleBackground: '#007bff',-->
|
||||
<!-- mainColor: '#007bff',-->
|
||||
<!-- placeholderText: 'Type your message here...',-->
|
||||
<!-- aboutText: 'Insurance Assistant ILA',-->
|
||||
<!-- enableAttachments: false,-->
|
||||
<!-- parameters: {-->
|
||||
<!-- employee_id: '12288',-->
|
||||
<!-- session_id: 'XYZ789',-->
|
||||
<!-- origin: 'mobile',-->
|
||||
<!-- emp_code: 'HTL-007',-->
|
||||
<!-- client_id: 159,-->
|
||||
<!-- client_branch_id: 125,-->
|
||||
<!-- },-->
|
||||
<!-- };-->
|
||||
|
||||
<!-- </script>-->
|
||||
|
||||
<!-- <script src="https://cdn.jsdelivr.net/npm/botman-web-widget@0/build/js/widget.js"></script>-->
|
||||
<!--</head>-->
|
||||
<!--<body></body>-->
|
||||
<!--</html>-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Ask ILA</title>
|
||||
<style>
|
||||
#botmanWidgetRoot svg {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="botmanWidgetRoot"></div>
|
||||
|
||||
<!--<script>-->
|
||||
<!-- function initBotman(params) {-->
|
||||
<!--<!– console.log('[Botman] Params:', params);–>-->
|
||||
<!-- console.log('[Botman] Received Params:', JSON.stringify(params, null, 2));-->
|
||||
|
||||
<!-- window.botmanWidget = {-->
|
||||
<!-- chatServer: 'https://venbait.in/nhance/dev/chat',-->
|
||||
<!-- frameEndpoint: 'https://venbait.in/nhance/dev/widget',-->
|
||||
<!-- title: 'Ask ILA',-->
|
||||
<!-- introMessage: '',-->
|
||||
<!-- bubbleBackground: '#007bff',-->
|
||||
<!-- mainColor: '#007bff',-->
|
||||
<!-- placeholderText: 'Type your message here...',-->
|
||||
<!-- aboutText: 'Insurance Assistant ILA',-->
|
||||
<!-- enableAttachments: false,-->
|
||||
<!-- parameters: params-->
|
||||
<!-- };-->
|
||||
|
||||
<!-- const script = document.createElement('script');-->
|
||||
<!-- script.src = "https://cdn.jsdelivr.net/npm/botman-web-widget@0/build/js/widget.js";-->
|
||||
|
||||
<!-- script.onload = () => {-->
|
||||
<!-- const checkWidget = setInterval(() => {-->
|
||||
<!-- if (typeof botmanChatWidget !== 'undefined') {-->
|
||||
<!-- botmanChatWidget.open();-->
|
||||
<!-- botmanChatWidget.sayAsBot(`Hi ${params.emp_name}, ILA here to assist you.`);-->
|
||||
<!-- clearInterval(checkWidget);-->
|
||||
<!-- }-->
|
||||
<!-- }, 300);-->
|
||||
<!-- };-->
|
||||
|
||||
<!-- document.body.appendChild(script);-->
|
||||
<!-- }-->
|
||||
<!--</script>-->
|
||||
|
||||
<script>
|
||||
function initBotman(params) {
|
||||
console.log('Params:', params);
|
||||
console.log('[Botman] Received Params:', JSON.stringify(params, null, 2));
|
||||
|
||||
window.botmanWidget = {
|
||||
chatServer: 'https://venbait.in/nhance/dev/chat',
|
||||
frameEndpoint: 'https://venbait.in/nhance/dev/widget',
|
||||
title: 'Ask ILA',
|
||||
introMessage: '', // Must be empty if using sayAsBot()
|
||||
bubbleBackground: '#007bff',
|
||||
mainColor: '#007bff',
|
||||
placeholderText: 'Type your message here...',
|
||||
aboutText: 'Insurance Assistant ILA',
|
||||
enableAttachments: false,
|
||||
parameters: params
|
||||
};
|
||||
|
||||
const script = document.createElement('script');
|
||||
script.src = "https://cdn.jsdelivr.net/npm/botman-web-widget@0/build/js/widget.js";
|
||||
|
||||
script.onload = function () {
|
||||
console.log("✅ Botman script loaded");
|
||||
|
||||
const waitForWidget = setInterval(() => {
|
||||
if (typeof botmanChatWidget !== 'undefined' && botmanChatWidget.open) {
|
||||
clearInterval(waitForWidget);
|
||||
|
||||
botmanChatWidget.open();
|
||||
|
||||
// Send default message after slight delay
|
||||
setTimeout(() => {
|
||||
botmanChatWidget.sayAsBot(`Hi ${params.emp_name || 'User'}, ILA here to assist you.`);
|
||||
}, 800); // Allow chat to render
|
||||
} else {
|
||||
console.log("⏳ Waiting for botmanChatWidget...");
|
||||
}
|
||||
}, 300);
|
||||
};
|
||||
|
||||
script.onerror = function () {
|
||||
console.error("❌ Failed to load Botman widget.js");
|
||||
};
|
||||
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
const entries = Object.fromEntries(queryParams.entries());
|
||||
document.write('<pre>' + JSON.stringify(entries, null, 2) + '</pre>');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
||||
BIN
assets/faqs.jpg
Normal file
BIN
assets/faqs.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 780 KiB |
@ -117,12 +117,25 @@ class _CustomAppBarState extends State<CustomAppBar> {
|
||||
context.push('/claims');
|
||||
}),
|
||||
),
|
||||
NavBarItem(
|
||||
text: "FAQs",
|
||||
onTap: () => handleMenuTap(() {
|
||||
context.push('/faqs');
|
||||
}),
|
||||
),
|
||||
NavBarItem(
|
||||
text: "Profile",
|
||||
onTap: () => handleMenuTap(() {
|
||||
context.push('/profile');
|
||||
}),
|
||||
),
|
||||
NavBarItem(
|
||||
text: "Help",
|
||||
onTap: () => handleMenuTap(() {
|
||||
context.push('/help');
|
||||
}),
|
||||
),
|
||||
|
||||
// NavBarItem(
|
||||
// text: "Wellness",
|
||||
// onTap: () => handleMenuTap(() {
|
||||
@ -133,12 +146,6 @@ class _CustomAppBarState extends State<CustomAppBar> {
|
||||
// );
|
||||
// }),
|
||||
// ),
|
||||
NavBarItem(
|
||||
text: "Profile",
|
||||
onTap: () => handleMenuTap(() {
|
||||
context.push('/profile');
|
||||
}),
|
||||
),
|
||||
NavBarItem(
|
||||
text: "Logout",
|
||||
onTap: () {
|
||||
|
||||
@ -14,6 +14,7 @@ import 'package:nhance_app_pwa/pages/postEnrollment/AddPolicyScreen.dart';
|
||||
import 'package:nhance_app_pwa/pages/postEnrollment/botman_chat.dart';
|
||||
import 'package:nhance_app_pwa/pages/postEnrollment/claimprocess.dart';
|
||||
import 'package:nhance_app_pwa/pages/postEnrollment/claims.dart';
|
||||
import 'package:nhance_app_pwa/pages/postEnrollment/faqs.dart';
|
||||
import 'package:nhance_app_pwa/pages/postEnrollment/generalexclusionsdeductibles.dart';
|
||||
import 'package:nhance_app_pwa/pages/postEnrollment/help.dart';
|
||||
import 'package:nhance_app_pwa/pages/postEnrollment/home.dart';
|
||||
@ -230,6 +231,10 @@ Future<void> startApp() async {
|
||||
path: '/empDetails',
|
||||
builder: (context, state) => empDetails(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/faqs',
|
||||
builder: (context, state) => faqs(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/addOnsDetails', builder: (context, state) => addOnsDetails()),
|
||||
GoRoute(
|
||||
|
||||
@ -1502,15 +1502,15 @@ class _loginState extends State<login> {
|
||||
value.isEmpty) {
|
||||
return 'Please enter your password';
|
||||
}
|
||||
if (value.length < 6) {
|
||||
return 'Password must be at least 6 characters';
|
||||
}
|
||||
// Optional strong password rule
|
||||
if (!RegExp(
|
||||
r'^(?=.*[A-Z])(?=.*[0-9]).{6,}$')
|
||||
.hasMatch(value)) {
|
||||
return 'Include at least 1 uppercase letter and 1 number';
|
||||
}
|
||||
// if (value.length < 6) {
|
||||
// return 'Password must be at least 6 characters';
|
||||
// }
|
||||
// // Optional strong password rule
|
||||
// if (!RegExp(
|
||||
// r'^(?=.*[A-Z])(?=.*[0-9]).{6,}$')
|
||||
// .hasMatch(value)) {
|
||||
// return 'Include at least 1 uppercase letter and 1 number';
|
||||
// }
|
||||
return null;
|
||||
},
|
||||
),
|
||||
|
||||
@ -367,17 +367,19 @@ class _AddPolicyScreenState extends State<AddPolicyScreen> {
|
||||
icons: [
|
||||
Icons.home_outlined,
|
||||
Icons.sticky_note_2_outlined,
|
||||
Icons.question_answer_outlined,
|
||||
Icons.person_outline_outlined,
|
||||
Icons.headset_mic_outlined,
|
||||
Icons.health_and_safety_outlined,
|
||||
],
|
||||
labels: ["Home", "Claims", "Profile", "Help", "Wellness"],
|
||||
initialIndex: 3,
|
||||
labels: ["Home", "Claims", "FAQs", "Profile", "Help"],
|
||||
initialIndex: 0,
|
||||
onTabChanged: (index) {
|
||||
if (index == 0) context.go('/home');
|
||||
if (index == 1) context.go('/claims');
|
||||
if (index == 2) context.go('/profile');
|
||||
if (index == 3) context.go('/help');
|
||||
if (index == 2) context.go('/faqs');
|
||||
if (index == 3) context.go('/profile');
|
||||
if (index == 4) {context.push('/help');
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -584,7 +584,7 @@ class _claimsState extends State<claims> {
|
||||
.isDesktop(
|
||||
context)
|
||||
? 140
|
||||
: 50,
|
||||
: 10 ,
|
||||
vertical: Responsive
|
||||
.isDesktop(
|
||||
context)
|
||||
@ -593,7 +593,7 @@ class _claimsState extends State<claims> {
|
||||
// primary: Color(0xFF000000),
|
||||
),
|
||||
child: Text(
|
||||
'Your Plan',
|
||||
'Register a Claim',
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: Responsive
|
||||
.isDesktop(
|
||||
@ -609,7 +609,7 @@ class _claimsState extends State<claims> {
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
'Your Plan',
|
||||
'Register a Claim',
|
||||
style:
|
||||
GoogleFonts.poppins(
|
||||
fontSize: Responsive
|
||||
@ -766,8 +766,10 @@ class _claimsState extends State<claims> {
|
||||
} else if (index == 1) {
|
||||
context.push('/claims');
|
||||
} else if (index == 2) {
|
||||
context.push('/profile');
|
||||
context.push('/faqs');
|
||||
} else if (index == 3) {
|
||||
context.push('/profile');
|
||||
} else if (index == 4) {
|
||||
context.push('/help');
|
||||
}
|
||||
// else if (index == 4) {
|
||||
@ -784,12 +786,12 @@ class _claimsState extends State<claims> {
|
||||
icons: [
|
||||
Icons.home_outlined,
|
||||
Icons.sticky_note_2_outlined,
|
||||
Icons.question_answer_outlined,
|
||||
Icons.person_outline_outlined,
|
||||
Icons.headset_mic_outlined,
|
||||
// Icons.health_and_safety_outlined,
|
||||
],
|
||||
labels: ["Home", "Claims", "Profile", "Help"],
|
||||
initialIndex: 0, // Initial index of the bottom navigation bar
|
||||
labels: ["Home", "Claims", "FAQs", "Profile","Help"],
|
||||
initialIndex: 1, // Initial index of the bottom navigation bar
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
692
lib/pages/postEnrollment/faqs.dart
Normal file
692
lib/pages/postEnrollment/faqs.dart
Normal file
@ -0,0 +1,692 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:jwt_decode/jwt_decode.dart';
|
||||
import 'package:nhance_app_pwa/pages/postEnrollment/service/api_service.dart';
|
||||
|
||||
import '../../customAppBar/customAppBar.dart';
|
||||
import '../../customAppBar/customFooter.dart';
|
||||
import '../../customAppBar/responsive.dart';
|
||||
import '../../customAppBar/tabs.dart';
|
||||
import '../../customAppBar/toastHelper.dart';
|
||||
import '../service/SessionManager.dart';
|
||||
import '../service/TokenService.dart';
|
||||
import '../service/data_manager.dart';
|
||||
import 'package:flutter_html/flutter_html.dart';
|
||||
|
||||
import '../service/video_player_widget.dart';
|
||||
|
||||
class faqs extends StatefulWidget {
|
||||
const faqs({super.key});
|
||||
|
||||
@override
|
||||
State<faqs> createState() => _faqsState();
|
||||
}
|
||||
|
||||
class FaqItem {
|
||||
final String id;
|
||||
final String category;
|
||||
final String question;
|
||||
final String? answer;
|
||||
|
||||
FaqItem({
|
||||
required this.id,
|
||||
required this.category,
|
||||
required this.question,
|
||||
this.answer,
|
||||
});
|
||||
}
|
||||
|
||||
class FaqNode {
|
||||
final String title;
|
||||
final List<FaqNode> children = [];
|
||||
final List<FaqItem> faqs = [];
|
||||
|
||||
FaqNode({required this.title});
|
||||
}
|
||||
|
||||
class _faqsState extends State<faqs> {
|
||||
bool isLoading = false;
|
||||
late ApiService apiService;
|
||||
dynamic empCodeString;
|
||||
dynamic empPrimaryId;
|
||||
dynamic empEmailID;
|
||||
dynamic client_id;
|
||||
dynamic client_branch_id;
|
||||
dynamic empMobileNo;
|
||||
final session = SessionManager();
|
||||
final dataManager = DataManager();
|
||||
|
||||
/// ---------------- STATE ----------------
|
||||
Map<String, FaqNode> faqTree = {};
|
||||
String selectedTab = 'Others';
|
||||
|
||||
String? openSection;
|
||||
String? openSubSection;
|
||||
String? openQuestionId;
|
||||
|
||||
/// ---------------- SAMPLE DATA (Replace with API) ----------------
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
apiService = ApiService(context);
|
||||
_loadToken();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _loadToken() async {
|
||||
print('_loadToken');
|
||||
final String? token = await TokenService.getPostToken();
|
||||
if (token != null && token.isNotEmpty) {
|
||||
// Decode the JWT token received from the API response
|
||||
Map<String, dynamic>? decodedToken = Jwt.parseJwt(token);
|
||||
print(decodedToken);
|
||||
empMobileNo = session.mobileNo ?? '';
|
||||
empEmailID = session.empEmailCorporate ?? '';
|
||||
client_branch_id = session.empClientBranchId;
|
||||
empCodeString = session.empCodeString;
|
||||
print(empCodeString); // Check if emp_code is correct
|
||||
empPrimaryId = session.empPrimaryId;
|
||||
client_id = session.client_id;
|
||||
print(client_id);
|
||||
getFAQsDetails();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getFAQsDetails() async {
|
||||
setState(() => isLoading = true);
|
||||
|
||||
try {
|
||||
final response = await apiService.getFAQsApiData();
|
||||
|
||||
if (response['status'] == 'success') {
|
||||
final List list = response['data']['faq_list'] ?? [];
|
||||
|
||||
// ✅ Convert API data → FaqItem list
|
||||
final List<FaqItem> apiFaqs =
|
||||
list.where((e) => e['is_active'] == '1').map<FaqItem>((e) {
|
||||
return FaqItem(
|
||||
id: e['id'].toString(),
|
||||
category: e['category']?.toString().trim() ?? '',
|
||||
question: e['question']?.toString() ?? '',
|
||||
answer: e['answer']?.toString(),
|
||||
);
|
||||
}).toList();
|
||||
|
||||
// ✅ Build tree
|
||||
final tree = buildFaqTree(apiFaqs);
|
||||
|
||||
setState(() {
|
||||
faqTree = tree;
|
||||
|
||||
// ✅ Set default tab safely
|
||||
if (faqTree.isNotEmpty) {
|
||||
selectedTab = faqTree.keys.first;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ToastHelper.showErrorToast(context, 'No FAQs found');
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('FAQ Error: $e');
|
||||
ToastHelper.showErrorToast(context, 'Failed to load FAQs');
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() => isLoading = false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ---------------- TREE BUILDER ----------------
|
||||
Map<String, FaqNode> buildFaqTree(List<FaqItem> faqs) {
|
||||
final Map<String, FaqNode> root = {};
|
||||
|
||||
for (var faq in faqs) {
|
||||
final parts = faq.category.split('/').map((e) => e.trim()).toList();
|
||||
|
||||
final main = parts.first;
|
||||
|
||||
root.putIfAbsent(main, () => FaqNode(title: main));
|
||||
FaqNode current = root[main]!;
|
||||
|
||||
for (int i = 1; i < parts.length; i++) {
|
||||
final part = parts[i];
|
||||
final existing =
|
||||
current.children.where((e) => e.title == part).toList();
|
||||
|
||||
if (existing.isEmpty) {
|
||||
final node = FaqNode(title: part);
|
||||
current.children.add(node);
|
||||
current = node;
|
||||
} else {
|
||||
current = existing.first;
|
||||
}
|
||||
}
|
||||
|
||||
current.faqs.add(faq);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
onPopInvokedWithResult: (didPop, result) {
|
||||
if (didPop) return;
|
||||
context.pop();
|
||||
},
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: CustomAppBar(),
|
||||
body: Stack(children: [
|
||||
SingleChildScrollView(
|
||||
child: Container(
|
||||
padding: Responsive.isDesktop(context)
|
||||
? EdgeInsets.symmetric(
|
||||
horizontal: MediaQuery.of(context).size.width *
|
||||
0.1, // 30% of screen width as horizontal padding
|
||||
vertical: MediaQuery.of(context).size.height *
|
||||
0.03, // 5% of screen height as vertical padding
|
||||
)
|
||||
: EdgeInsets.all(10),
|
||||
color: Colors.white,
|
||||
child: Column(children: [
|
||||
Container(
|
||||
padding: Responsive.isDesktop(context)
|
||||
? EdgeInsets.only(
|
||||
top: 15, bottom: 15, left: 25, right: 25)
|
||||
: EdgeInsets.only(top: 0, bottom: 0, left: 0, right: 0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 12,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
context.pop();
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Icon(
|
||||
Icons
|
||||
.chevron_left, // Replace with your desired icon
|
||||
color: Color(0xFF000000),
|
||||
size: 30,
|
||||
),
|
||||
SizedBox(
|
||||
width:
|
||||
5), // Adjust space between icon and text
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'FAQs',
|
||||
textAlign: TextAlign.start,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF000000),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Frequently Asked Questions',
|
||||
textAlign: TextAlign.start,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize:
|
||||
12, // Adjust the font size as needed
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Color(0xFF000000),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFEEF5FF),
|
||||
// Set background color for the container
|
||||
borderRadius: BorderRadius.circular(
|
||||
5), // Set border radius for the container
|
||||
),
|
||||
padding: Responsive.isDesktop(context)
|
||||
? EdgeInsets.only(
|
||||
top: 10, bottom: 10, left: 25, right: 25)
|
||||
: EdgeInsets.only(
|
||||
top: 10, bottom: 10, left: 10, right: 10),
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft, // ✅ LEFT ALIGN TABS
|
||||
child: Responsive.isDesktop(context)
|
||||
? buildTabs() // no scroll on web
|
||||
: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: buildTabs(),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
buildContent(),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// NhanceVideoWrapper(context)
|
||||
|
||||
],
|
||||
),
|
||||
),
|
||||
]),
|
||||
)),
|
||||
if (isLoading)
|
||||
Container(
|
||||
color: Color(0x98FFFCE5), // Semi-transparent background
|
||||
child: Center(
|
||||
child: // Your GIF loader widget
|
||||
Image.asset(
|
||||
height: 60,
|
||||
width: 60,
|
||||
'assets/nhance-loader.gif'), // Adjust path to your GIF loader
|
||||
),
|
||||
),
|
||||
if (Responsive.isDesktop(context))
|
||||
Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Container(
|
||||
width: double.infinity, // Make the footer full width
|
||||
child: CustomFooter(),
|
||||
),
|
||||
),
|
||||
]),
|
||||
// floatingActionButton: Responsive.isDesktop(context)
|
||||
// ? null
|
||||
// : FloatingActionButton(
|
||||
// onPressed: () {
|
||||
// Navigator.pushNamed(context, 'chatbot');
|
||||
// },
|
||||
// child: Icon(Icons.chat),
|
||||
// ),
|
||||
floatingActionButtonLocation: Responsive.isDesktop(context)
|
||||
? null
|
||||
: FloatingActionButtonLocation.miniEndFloat,
|
||||
bottomNavigationBar: Responsive.isDesktop(context)
|
||||
? null
|
||||
: CustomBottomNavigationBar(
|
||||
onTabChanged: (index) {
|
||||
// Add your navigation logic here
|
||||
// repositionBotman();
|
||||
if (index == 0) {
|
||||
context.push('/home');
|
||||
} else if (index == 1) {
|
||||
context.push('/claims');
|
||||
} else if (index == 2) {
|
||||
context.push('/faqs');
|
||||
} else if (index == 3) {
|
||||
context.push('/profile');
|
||||
} else if (index == 4) {
|
||||
context.push('/help');
|
||||
}
|
||||
// else if (index == 4) {
|
||||
// // context.push('/wellness');
|
||||
// // Wellness tab clicked → show popup
|
||||
// if(!isRetailLoggedIn)
|
||||
// PopupHelper.showRedirectPopup(
|
||||
// context: context,
|
||||
// apiService: apiService,
|
||||
// empPrimaryId: session.empPrimaryId,
|
||||
// );
|
||||
// }
|
||||
},
|
||||
icons: [
|
||||
Icons.home_outlined,
|
||||
Icons.sticky_note_2_outlined,
|
||||
Icons.question_answer_outlined,
|
||||
Icons.person_outline_outlined,
|
||||
Icons.headset_mic_outlined,
|
||||
],
|
||||
labels: ["Home", "Claims", "FAQs", "Profile","Help"],
|
||||
initialIndex: 2, // Initial index of the bottom navigation bar
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
/// ---------------- MAIN TABS ----------------
|
||||
Widget buildTabs() {
|
||||
final bool isDesktop = Responsive.isDesktop(context);
|
||||
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: faqTree.keys.map((tab) {
|
||||
final active = selectedTab == tab;
|
||||
|
||||
final tabWidget = InkWell(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
selectedTab = tab;
|
||||
openSection = null;
|
||||
openSubSection = null;
|
||||
openQuestionId = null;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: active ? Colors.deepOrange : Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: active ? Colors.deepOrange : const Color(0xFFE0E0E0),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
tab,
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
color: active ? Colors.white : Colors.deepOrange,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
/// 🔥 DESKTOP → FULL WIDTH TAB
|
||||
if (isDesktop) {
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 10),
|
||||
child: tabWidget,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 📱 MOBILE → CONTENT WIDTH TAB
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 8),
|
||||
child: SizedBox(
|
||||
width: 110,
|
||||
child: tabWidget,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// ---------------- CONTENT ----------------
|
||||
Widget buildContent() {
|
||||
final node = faqTree[selectedTab];
|
||||
|
||||
if (node == null) {
|
||||
return const Center(child: Text('No FAQs'));
|
||||
}
|
||||
|
||||
// Health / Travel without hierarchy
|
||||
if (node.children.isEmpty) {
|
||||
return Column(
|
||||
children: node.faqs.map(buildQuestion).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
// Others → Fire Insurance → General
|
||||
return Column(
|
||||
children: node.children.map(buildSection).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// ---------------- SECTION ----------------
|
||||
Widget buildSection(FaqNode node) {
|
||||
final bool isOpen = openSection == node.title;
|
||||
const Duration kExpandDuration = Duration(milliseconds: 450);
|
||||
const Curve kExpandCurve = Curves.easeInOutCubic;
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
openSection = isOpen ? null : node.title;
|
||||
openSubSection = null;
|
||||
openQuestionId = null;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(color: Color(0xFFE0E0E0)),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
node.title,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
AnimatedRotation(
|
||||
turns: isOpen ? 0.5 : 0,
|
||||
duration: kExpandDuration,
|
||||
curve: kExpandCurve,
|
||||
child: const Icon(
|
||||
Icons.keyboard_arrow_down,
|
||||
color: Colors.deepOrange,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
/// SECTION CONTENT
|
||||
AnimatedSize(
|
||||
duration: kExpandDuration,
|
||||
curve: kExpandCurve,
|
||||
child: isOpen
|
||||
? Column(
|
||||
children: [
|
||||
if (node.children.isNotEmpty)
|
||||
...node.children.map(buildSubSection),
|
||||
if (node.children.isEmpty) ...node.faqs.map(buildQuestion),
|
||||
],
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// ---------------- SUB SECTION ----------------
|
||||
Widget buildSubSection(FaqNode node) {
|
||||
final bool isOpen = openSubSection == node.title;
|
||||
const Duration kExpandDuration = Duration(milliseconds: 450);
|
||||
const Curve kExpandCurve = Curves.easeInOutCubic;
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
openSubSection = isOpen ? null : node.title;
|
||||
openQuestionId = null;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(color: Color(0xFFEDEDED)),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
node.title,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
AnimatedRotation(
|
||||
turns: isOpen ? 0.5 : 0,
|
||||
duration: kExpandDuration,
|
||||
curve: kExpandCurve,
|
||||
child: const Icon(
|
||||
Icons.keyboard_arrow_down,
|
||||
size: 20,
|
||||
color: Colors.deepOrange,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
AnimatedSize(
|
||||
duration: kExpandDuration,
|
||||
curve: kExpandCurve,
|
||||
child: isOpen
|
||||
? Column(
|
||||
children: node.faqs.map(buildQuestion).toList(),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// ---------------- QUESTION ----------------
|
||||
Widget buildQuestion(FaqItem faq) {
|
||||
final bool isOpen = openQuestionId == faq.id;
|
||||
const Duration kExpandDuration = Duration(milliseconds: 450);
|
||||
const Curve kExpandCurve = Curves.easeInOutCubic;
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
openQuestionId = isOpen ? null : faq.id;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(color: Color(0xFFF0F0F0)),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
faq.question,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
AnimatedRotation(
|
||||
turns: isOpen ? 0.5 : 0,
|
||||
duration: kExpandDuration,
|
||||
curve: kExpandCurve,
|
||||
child: const Icon(
|
||||
Icons.keyboard_arrow_down,
|
||||
size: 18,
|
||||
color: Colors.deepOrange,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
AnimatedSize(
|
||||
duration: kExpandDuration,
|
||||
curve: kExpandCurve,
|
||||
child: isOpen
|
||||
? Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.fromLTRB(16, 12, 16, 16),
|
||||
color: const Color(0xFFFAFAFA),
|
||||
child: Html(
|
||||
data: faq.answer ?? '',
|
||||
style: {
|
||||
"body": Style(
|
||||
margin: Margins.zero,
|
||||
padding: HtmlPaddings.zero,
|
||||
fontSize: FontSize(13),
|
||||
color: Colors.black87,
|
||||
),
|
||||
"table": Style(
|
||||
border: Border.all(color: Colors.grey.shade300),
|
||||
),
|
||||
"td": Style(
|
||||
padding: HtmlPaddings.all(8),
|
||||
border: Border.all(color: Colors.grey.shade300),
|
||||
),
|
||||
},
|
||||
),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// ---------------- ARROW ----------------
|
||||
Widget buildArrow(bool open) {
|
||||
return AnimatedRotation(
|
||||
turns: open ? 0.5 : 0,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: const Icon(
|
||||
Icons.keyboard_arrow_down,
|
||||
color: Colors.deepOrange,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Widget NhanceVideoWrapper(BuildContext context) {
|
||||
final video = NetworkVideoPlayer(
|
||||
videoUrl: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
|
||||
);
|
||||
|
||||
if (Responsive.isDesktop(context)) {
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
width: 500,
|
||||
height: 250,
|
||||
child: video,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 📱 Mobile → default behavior
|
||||
return video;
|
||||
}
|
||||
|
||||
}
|
||||
@ -806,7 +806,7 @@ class _generalExclusionsDeductiblesState
|
||||
: FloatingActionButtonLocation.miniEndFloat,
|
||||
bottomNavigationBar: Responsive.isDesktop(context)
|
||||
? null
|
||||
: CustomBottomNavigationBar(
|
||||
: CustomBottomNavigationBar(
|
||||
onTabChanged: (index) {
|
||||
// Add your navigation logic here
|
||||
// repositionBotman();
|
||||
@ -815,8 +815,10 @@ class _generalExclusionsDeductiblesState
|
||||
} else if (index == 1) {
|
||||
context.push('/claims');
|
||||
} else if (index == 2) {
|
||||
context.push('/profile');
|
||||
context.push('/faqs');
|
||||
} else if (index == 3) {
|
||||
context.push('/profile');
|
||||
} else if (index == 4) {
|
||||
context.push('/help');
|
||||
}
|
||||
// else if (index == 4) {
|
||||
@ -833,11 +835,11 @@ class _generalExclusionsDeductiblesState
|
||||
icons: [
|
||||
Icons.home_outlined,
|
||||
Icons.sticky_note_2_outlined,
|
||||
Icons.question_answer_outlined,
|
||||
Icons.person_outline_outlined,
|
||||
Icons.headset_mic_outlined,
|
||||
// Icons.health_and_safety_outlined,
|
||||
],
|
||||
labels: ["Home", "Claims", "Profile", "Help"],
|
||||
labels: ["Home", "Claims", "FAQs", "Profile","Help"],
|
||||
initialIndex: 0, // Initial index of the bottom navigation bar
|
||||
),
|
||||
));
|
||||
|
||||
@ -583,6 +583,90 @@ class _helpState extends State<help> {
|
||||
),
|
||||
|
||||
SizedBox(height: 20),
|
||||
|
||||
InkWell(
|
||||
onTap: () {
|
||||
context.go('/faqs');
|
||||
},
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
height: Responsive.isDesktop(context) ? 140 : 120,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
border: Border.all(
|
||||
color: const Color(0xFF2F80ED),
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
/// LEFT IMAGE BLOCK
|
||||
Container(
|
||||
width: Responsive.isDesktop(context) ? 180 : 120,
|
||||
height: double.infinity,
|
||||
color: Colors.white,
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Image.asset(
|
||||
'assets/faqs.jpg',
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
|
||||
/// RIGHT CONTENT BLOCK
|
||||
Expanded(
|
||||
child: Container(
|
||||
color: const Color(0xFF93C01F),
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: Responsive.isDesktop(context) ? 32 : 16,
|
||||
vertical: Responsive.isDesktop(context) ? 24 : 16,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
/// TEXT CONTENT
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'FAQs',
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize:
|
||||
Responsive.isDesktop(context) ? 36 : 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Find answers to commonly asked questions',
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize:
|
||||
Responsive.isDesktop(context) ? 18 : 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
/// RIGHT ARROW
|
||||
Icon(
|
||||
Icons.chevron_right,
|
||||
size: Responsive.isDesktop(context) ? 36 : 28,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
// SizedBox(height: 15),
|
||||
// Container(
|
||||
// decoration: BoxDecoration(
|
||||
@ -1045,7 +1129,7 @@ class _helpState extends State<help> {
|
||||
: FloatingActionButtonLocation.miniEndFloat,
|
||||
bottomNavigationBar: Responsive.isDesktop(context)
|
||||
? null
|
||||
: CustomBottomNavigationBar(
|
||||
: CustomBottomNavigationBar(
|
||||
onTabChanged: (index) {
|
||||
// Add your navigation logic here
|
||||
// repositionBotman();
|
||||
@ -1054,8 +1138,10 @@ class _helpState extends State<help> {
|
||||
} else if (index == 1) {
|
||||
context.push('/claims');
|
||||
} else if (index == 2) {
|
||||
context.push('/profile');
|
||||
context.push('/faqs');
|
||||
} else if (index == 3) {
|
||||
context.push('/profile');
|
||||
} else if (index == 4) {
|
||||
context.push('/help');
|
||||
}
|
||||
// else if (index == 4) {
|
||||
@ -1072,12 +1158,12 @@ class _helpState extends State<help> {
|
||||
icons: [
|
||||
Icons.home_outlined,
|
||||
Icons.sticky_note_2_outlined,
|
||||
Icons.question_answer_outlined,
|
||||
Icons.person_outline_outlined,
|
||||
Icons.headset_mic_outlined,
|
||||
// Icons.health_and_safety_outlined,
|
||||
],
|
||||
labels: ["Home", "Claims", "Profile", "Help"],
|
||||
initialIndex: 0, // Initial index of the bottom navigation bar
|
||||
labels: ["Home", "Claims", "FAQs", "Profile","Help"],
|
||||
initialIndex: 4, // Initial index of the bottom navigation bar
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
@ -276,19 +276,19 @@ class _HomeState extends State<Home> {
|
||||
emp_name = response['emp_name'];
|
||||
policyList = List.from(response['data'] ?? []);
|
||||
retailPolicyDetails = List.from(response['retail_policy_data'] ?? []);
|
||||
print('policyList $policyList');
|
||||
pre_policy_count = response['pre_policy_count'] ?? '';
|
||||
final empNotEnrolledCount = response['emp_not_enrolled_count'] ?? '';
|
||||
final wellnessStatus = response['wellness_data']['status'] ?? 'failed';
|
||||
print('wellnessStatus $wellnessStatus');
|
||||
if (wellnessStatus == 'success') {
|
||||
wellnessURL = response['wellness_data']['data'];
|
||||
}
|
||||
print('policyList $policyList');
|
||||
pre_policy_count = response['pre_policy_count'] ?? '';
|
||||
final empNotEnrolledCount = response['emp_not_enrolled_count'] ?? '';
|
||||
final wellnessStatus = response['wellness_data']['status'] ?? 'failed';
|
||||
print('wellnessStatus $wellnessStatus');
|
||||
if (wellnessStatus == 'success') {
|
||||
wellnessURL = response['wellness_data']['data'];
|
||||
}
|
||||
|
||||
setState(() {
|
||||
isTokenAvailable = pre_policy_count != 0;
|
||||
emp_not_enrolled_count = empNotEnrolledCount != 0;
|
||||
});
|
||||
setState(() {
|
||||
isTokenAvailable = pre_policy_count != 0;
|
||||
emp_not_enrolled_count = empNotEnrolledCount != 0;
|
||||
});
|
||||
|
||||
if (emp_not_enrolled_count) {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
@ -630,7 +630,7 @@ class _HomeState extends State<Home> {
|
||||
},
|
||||
),
|
||||
SizedBox(height: 15),
|
||||
if(!isRetailLoggedIn)
|
||||
if(!isRetailLoggedIn && isTokenAvailable)
|
||||
Card(
|
||||
elevation: 2,
|
||||
color: Color(0xFFDDF3FF),
|
||||
@ -1189,40 +1189,42 @@ class _HomeState extends State<Home> {
|
||||
: FloatingActionButtonLocation.miniEndFloat,
|
||||
bottomNavigationBar: Responsive.isDesktop(context)
|
||||
? null
|
||||
: CustomBottomNavigationBar(
|
||||
onTabChanged: (index) {
|
||||
// Add your navigation logic here
|
||||
// repositionBotman();
|
||||
if (index == 0) {
|
||||
if(!isRetailLoggedIn) context.push('/home');
|
||||
} else if (index == 1) {
|
||||
if(!isRetailLoggedIn) context.push('/claims');
|
||||
} else if (index == 2) {
|
||||
if(!isRetailLoggedIn) context.push('/profile');
|
||||
} else if (index == 3) {
|
||||
if(!isRetailLoggedIn) context.push('/help');
|
||||
}
|
||||
// else if (index == 4) {
|
||||
// // context.push('/wellness');
|
||||
// // Wellness tab clicked → show popup
|
||||
// if(!isRetailLoggedIn)
|
||||
// PopupHelper.showRedirectPopup(
|
||||
// context: context,
|
||||
// apiService: apiService,
|
||||
// empPrimaryId: session.empPrimaryId,
|
||||
// );
|
||||
// }
|
||||
},
|
||||
icons: [
|
||||
Icons.home_outlined,
|
||||
Icons.sticky_note_2_outlined,
|
||||
Icons.person_outline_outlined,
|
||||
Icons.headset_mic_outlined,
|
||||
// Icons.health_and_safety_outlined,
|
||||
],
|
||||
labels: ["Home", "Claims", "Profile", "Help"],
|
||||
initialIndex: 0, // Initial index of the bottom navigation bar
|
||||
),
|
||||
: CustomBottomNavigationBar(
|
||||
onTabChanged: (index) {
|
||||
// Add your navigation logic here
|
||||
// repositionBotman();
|
||||
if (index == 0) {
|
||||
context.push('/home');
|
||||
} else if (index == 1) {
|
||||
context.push('/claims');
|
||||
} else if (index == 2) {
|
||||
context.push('/faqs');
|
||||
} else if (index == 3) {
|
||||
context.push('/profile');
|
||||
} else if (index == 4) {
|
||||
context.push('/help');
|
||||
}
|
||||
// else if (index == 4) {
|
||||
// // context.push('/wellness');
|
||||
// // Wellness tab clicked → show popup
|
||||
// if(!isRetailLoggedIn)
|
||||
// PopupHelper.showRedirectPopup(
|
||||
// context: context,
|
||||
// apiService: apiService,
|
||||
// empPrimaryId: session.empPrimaryId,
|
||||
// );
|
||||
// }
|
||||
},
|
||||
icons: [
|
||||
Icons.home_outlined,
|
||||
Icons.sticky_note_2_outlined,
|
||||
Icons.question_answer_outlined,
|
||||
Icons.person_outline_outlined,
|
||||
Icons.headset_mic_outlined,
|
||||
],
|
||||
labels: ["Home", "Claims", "FAQs", "Profile","Help"],
|
||||
initialIndex: 0, // Initial index of the bottom navigation bar
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
@ -1882,6 +1884,11 @@ class _HomeState extends State<Home> {
|
||||
}
|
||||
|
||||
Widget _buildCarouselSlider(List<Widget> cards) {
|
||||
final int totalCards = cards.length;
|
||||
final bool hasAddCard = isActive; // because you add AddCard only when isActive
|
||||
final int displayCardCount =
|
||||
hasAddCard ? totalCards - 1 : totalCards;
|
||||
|
||||
return Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
@ -1897,13 +1904,17 @@ class _HomeState extends State<Home> {
|
||||
aspectRatio: 2.0,
|
||||
initialPage: 0,
|
||||
onPageChanged: (index, reason) {
|
||||
setState(() => currentIndex = index);
|
||||
setState(() {
|
||||
currentIndex = index >= displayCardCount
|
||||
? displayCardCount - 1
|
||||
: index;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
// 🔵 CARD COUNTER (4/10)
|
||||
if(isActive)
|
||||
if(isActive && displayCardCount > 0)
|
||||
Positioned(
|
||||
bottom: 10,
|
||||
child: Container(
|
||||
@ -1913,7 +1924,7 @@ class _HomeState extends State<Home> {
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Text(
|
||||
"${currentIndex + 1} / ${cards.length}",
|
||||
"${(currentIndex + 1).clamp(1, displayCardCount)} / $displayCardCount",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 12,
|
||||
@ -2158,7 +2169,7 @@ class _HomeState extends State<Home> {
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
"OPT & Wellness",
|
||||
"OPD & Wellness",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
|
||||
@ -1455,7 +1455,7 @@ SizedBox(height: 15),
|
||||
: FloatingActionButtonLocation.miniEndFloat,
|
||||
bottomNavigationBar: Responsive.isDesktop(context)
|
||||
? null
|
||||
: CustomBottomNavigationBar(
|
||||
: CustomBottomNavigationBar(
|
||||
onTabChanged: (index) {
|
||||
// Add your navigation logic here
|
||||
// repositionBotman();
|
||||
@ -1464,8 +1464,10 @@ SizedBox(height: 15),
|
||||
} else if (index == 1) {
|
||||
context.push('/claims');
|
||||
} else if (index == 2) {
|
||||
context.push('/profile');
|
||||
context.push('/faqs');
|
||||
} else if (index == 3) {
|
||||
context.push('/profile');
|
||||
} else if (index == 4) {
|
||||
context.push('/help');
|
||||
}
|
||||
// else if (index == 4) {
|
||||
@ -1482,11 +1484,11 @@ SizedBox(height: 15),
|
||||
icons: [
|
||||
Icons.home_outlined,
|
||||
Icons.sticky_note_2_outlined,
|
||||
Icons.question_answer_outlined,
|
||||
Icons.person_outline_outlined,
|
||||
Icons.headset_mic_outlined,
|
||||
// Icons.health_and_safety_outlined,
|
||||
],
|
||||
labels: ["Home", "Claims", "Profile", "Help"],
|
||||
labels: ["Home", "Claims", "FAQs", "Profile","Help"],
|
||||
initialIndex: 0, // Initial index of the bottom navigation bar
|
||||
),
|
||||
)
|
||||
|
||||
@ -1105,7 +1105,7 @@ class _policiesState extends State<policies> {
|
||||
: FloatingActionButtonLocation.miniEndFloat,
|
||||
bottomNavigationBar: Responsive.isDesktop(context)
|
||||
? null
|
||||
: CustomBottomNavigationBar(
|
||||
: CustomBottomNavigationBar(
|
||||
onTabChanged: (index) {
|
||||
// Add your navigation logic here
|
||||
// repositionBotman();
|
||||
@ -1114,8 +1114,10 @@ class _policiesState extends State<policies> {
|
||||
} else if (index == 1) {
|
||||
context.push('/claims');
|
||||
} else if (index == 2) {
|
||||
context.push('/profile');
|
||||
context.push('/faqs');
|
||||
} else if (index == 3) {
|
||||
context.push('/profile');
|
||||
} else if (index == 4) {
|
||||
context.push('/help');
|
||||
}
|
||||
// else if (index == 4) {
|
||||
@ -1132,11 +1134,11 @@ class _policiesState extends State<policies> {
|
||||
icons: [
|
||||
Icons.home_outlined,
|
||||
Icons.sticky_note_2_outlined,
|
||||
Icons.question_answer_outlined,
|
||||
Icons.person_outline_outlined,
|
||||
Icons.headset_mic_outlined,
|
||||
// Icons.health_and_safety_outlined,
|
||||
],
|
||||
labels: ["Home", "Claims", "Profile", "Help"],
|
||||
labels: ["Home", "Claims", "FAQs", "Profile","Help"],
|
||||
initialIndex: 0, // Initial index of the bottom navigation bar
|
||||
),
|
||||
)
|
||||
|
||||
@ -888,7 +888,7 @@ class _profileState extends State<profile> {
|
||||
: FloatingActionButtonLocation.miniEndFloat,
|
||||
bottomNavigationBar: Responsive.isDesktop(context)
|
||||
? null
|
||||
: CustomBottomNavigationBar(
|
||||
: CustomBottomNavigationBar(
|
||||
onTabChanged: (index) {
|
||||
// Add your navigation logic here
|
||||
// repositionBotman();
|
||||
@ -897,8 +897,10 @@ class _profileState extends State<profile> {
|
||||
} else if (index == 1) {
|
||||
context.push('/claims');
|
||||
} else if (index == 2) {
|
||||
context.push('/profile');
|
||||
context.push('/faqs');
|
||||
} else if (index == 3) {
|
||||
context.push('/profile');
|
||||
} else if (index == 4) {
|
||||
context.push('/help');
|
||||
}
|
||||
// else if (index == 4) {
|
||||
@ -915,12 +917,12 @@ class _profileState extends State<profile> {
|
||||
icons: [
|
||||
Icons.home_outlined,
|
||||
Icons.sticky_note_2_outlined,
|
||||
Icons.question_answer_outlined,
|
||||
Icons.person_outline_outlined,
|
||||
Icons.headset_mic_outlined,
|
||||
// Icons.health_and_safety_outlined,
|
||||
],
|
||||
labels: ["Home", "Claims", "Profile", "Help"],
|
||||
initialIndex: 0, // Initial index of the bottom navigation bar
|
||||
labels: ["Home", "Claims", "FAQs", "Profile","Help"],
|
||||
initialIndex: 3, // Initial index of the bottom navigation bar
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
@ -303,7 +303,7 @@ class _raisedTicketHistoryState extends State<raisedTicketHistory> {
|
||||
: FloatingActionButtonLocation.miniEndFloat,
|
||||
bottomNavigationBar: Responsive.isDesktop(context)
|
||||
? null
|
||||
: CustomBottomNavigationBar(
|
||||
: CustomBottomNavigationBar(
|
||||
onTabChanged: (index) {
|
||||
// Add your navigation logic here
|
||||
// repositionBotman();
|
||||
@ -312,8 +312,10 @@ class _raisedTicketHistoryState extends State<raisedTicketHistory> {
|
||||
} else if (index == 1) {
|
||||
context.push('/claims');
|
||||
} else if (index == 2) {
|
||||
context.push('/profile');
|
||||
context.push('/faqs');
|
||||
} else if (index == 3) {
|
||||
context.push('/profile');
|
||||
} else if (index == 4) {
|
||||
context.push('/help');
|
||||
}
|
||||
// else if (index == 4) {
|
||||
@ -330,11 +332,11 @@ class _raisedTicketHistoryState extends State<raisedTicketHistory> {
|
||||
icons: [
|
||||
Icons.home_outlined,
|
||||
Icons.sticky_note_2_outlined,
|
||||
Icons.question_answer_outlined,
|
||||
Icons.person_outline_outlined,
|
||||
Icons.headset_mic_outlined,
|
||||
// Icons.health_and_safety_outlined,
|
||||
],
|
||||
labels: ["Home", "Claims", "Profile", "Help"],
|
||||
labels: ["Home", "Claims", "FAQs", "Profile","Help"],
|
||||
initialIndex: 0, // Initial index of the bottom navigation bar
|
||||
),
|
||||
)
|
||||
|
||||
@ -468,7 +468,7 @@ class _retailClaimFormsState extends State<retailClaimForm> {
|
||||
: FloatingActionButtonLocation.miniEndFloat,
|
||||
bottomNavigationBar: Responsive.isDesktop(context)
|
||||
? null
|
||||
: CustomBottomNavigationBar(
|
||||
: CustomBottomNavigationBar(
|
||||
onTabChanged: (index) {
|
||||
// Add your navigation logic here
|
||||
// repositionBotman();
|
||||
@ -477,8 +477,10 @@ class _retailClaimFormsState extends State<retailClaimForm> {
|
||||
} else if (index == 1) {
|
||||
context.push('/claims');
|
||||
} else if (index == 2) {
|
||||
context.push('/profile');
|
||||
context.push('/faqs');
|
||||
} else if (index == 3) {
|
||||
context.push('/profile');
|
||||
} else if (index == 4) {
|
||||
context.push('/help');
|
||||
}
|
||||
// else if (index == 4) {
|
||||
@ -495,11 +497,11 @@ class _retailClaimFormsState extends State<retailClaimForm> {
|
||||
icons: [
|
||||
Icons.home_outlined,
|
||||
Icons.sticky_note_2_outlined,
|
||||
Icons.question_answer_outlined,
|
||||
Icons.person_outline_outlined,
|
||||
Icons.headset_mic_outlined,
|
||||
// Icons.health_and_safety_outlined,
|
||||
],
|
||||
labels: ["Home", "Claims", "Profile", "Help"],
|
||||
labels: ["Home", "Claims", "FAQs", "Profile","Help"],
|
||||
initialIndex: 0, // Initial index of the bottom navigation bar
|
||||
),
|
||||
));
|
||||
|
||||
@ -93,6 +93,19 @@ class ApiService {
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getFAQsApiData() async {
|
||||
print(_postToken);
|
||||
if (_postToken == null) {
|
||||
await _initializeToken();
|
||||
}
|
||||
final url = Uri.parse('${Environment.apiUrl}FAQ');
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $_postToken' ?? '',
|
||||
};
|
||||
final response = await _makeGetRequest(url, headers);
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getTrackClaimsList(String empPrimaryId,mobileNo,emailID) async {
|
||||
print(_postToken);
|
||||
if (_postToken == null) {
|
||||
|
||||
@ -603,7 +603,7 @@ class _ticketsState extends State<tickets> {
|
||||
: FloatingActionButtonLocation.miniEndFloat,
|
||||
bottomNavigationBar: Responsive.isDesktop(context)
|
||||
? null
|
||||
: CustomBottomNavigationBar(
|
||||
: CustomBottomNavigationBar(
|
||||
onTabChanged: (index) {
|
||||
// Add your navigation logic here
|
||||
// repositionBotman();
|
||||
@ -612,8 +612,10 @@ class _ticketsState extends State<tickets> {
|
||||
} else if (index == 1) {
|
||||
context.push('/claims');
|
||||
} else if (index == 2) {
|
||||
context.push('/profile');
|
||||
context.push('/faqs');
|
||||
} else if (index == 3) {
|
||||
context.push('/profile');
|
||||
} else if (index == 4) {
|
||||
context.push('/help');
|
||||
}
|
||||
// else if (index == 4) {
|
||||
@ -630,11 +632,11 @@ class _ticketsState extends State<tickets> {
|
||||
icons: [
|
||||
Icons.home_outlined,
|
||||
Icons.sticky_note_2_outlined,
|
||||
Icons.question_answer_outlined,
|
||||
Icons.person_outline_outlined,
|
||||
Icons.headset_mic_outlined,
|
||||
// Icons.health_and_safety_outlined,
|
||||
],
|
||||
labels: ["Home", "Claims", "Profile", "Help"],
|
||||
labels: ["Home", "Claims", "FAQs", "Profile","Help"],
|
||||
initialIndex: 0, // Initial index of the bottom navigation bar
|
||||
),
|
||||
));
|
||||
|
||||
@ -674,7 +674,7 @@ class _tickettracklistState extends State<tickettracklist> {
|
||||
: FloatingActionButtonLocation.miniEndFloat,
|
||||
bottomNavigationBar: Responsive.isDesktop(context)
|
||||
? null
|
||||
: CustomBottomNavigationBar(
|
||||
: CustomBottomNavigationBar(
|
||||
onTabChanged: (index) {
|
||||
// Add your navigation logic here
|
||||
// repositionBotman();
|
||||
@ -683,8 +683,10 @@ class _tickettracklistState extends State<tickettracklist> {
|
||||
} else if (index == 1) {
|
||||
context.push('/claims');
|
||||
} else if (index == 2) {
|
||||
context.push('/profile');
|
||||
context.push('/faqs');
|
||||
} else if (index == 3) {
|
||||
context.push('/profile');
|
||||
} else if (index == 4) {
|
||||
context.push('/help');
|
||||
}
|
||||
// else if (index == 4) {
|
||||
@ -701,11 +703,11 @@ class _tickettracklistState extends State<tickettracklist> {
|
||||
icons: [
|
||||
Icons.home_outlined,
|
||||
Icons.sticky_note_2_outlined,
|
||||
Icons.question_answer_outlined,
|
||||
Icons.person_outline_outlined,
|
||||
Icons.headset_mic_outlined,
|
||||
// Icons.health_and_safety_outlined,
|
||||
],
|
||||
labels: ["Home", "Claims", "Profile", "Help"],
|
||||
labels: ["Home", "Claims", "FAQs", "Profile","Help"],
|
||||
initialIndex: 0, // Initial index of the bottom navigation bar
|
||||
),
|
||||
)
|
||||
|
||||
@ -364,7 +364,7 @@ class _wellnessState extends State<wellness> {
|
||||
: FloatingActionButtonLocation.miniEndFloat,
|
||||
bottomNavigationBar: Responsive.isDesktop(context)
|
||||
? null
|
||||
: CustomBottomNavigationBar(
|
||||
: CustomBottomNavigationBar(
|
||||
onTabChanged: (index) {
|
||||
// Add your navigation logic here
|
||||
// repositionBotman();
|
||||
@ -373,8 +373,10 @@ class _wellnessState extends State<wellness> {
|
||||
} else if (index == 1) {
|
||||
context.push('/claims');
|
||||
} else if (index == 2) {
|
||||
context.push('/profile');
|
||||
context.push('/faqs');
|
||||
} else if (index == 3) {
|
||||
context.push('/profile');
|
||||
} else if (index == 4) {
|
||||
context.push('/help');
|
||||
}
|
||||
// else if (index == 4) {
|
||||
@ -391,11 +393,11 @@ class _wellnessState extends State<wellness> {
|
||||
icons: [
|
||||
Icons.home_outlined,
|
||||
Icons.sticky_note_2_outlined,
|
||||
Icons.question_answer_outlined,
|
||||
Icons.person_outline_outlined,
|
||||
Icons.headset_mic_outlined,
|
||||
// Icons.health_and_safety_outlined,
|
||||
],
|
||||
labels: ["Home", "Claims", "Profile", "Help"],
|
||||
labels: ["Home", "Claims", "FAQs", "Profile","Help"],
|
||||
initialIndex: 0, // Initial index of the bottom navigation bar
|
||||
),
|
||||
)
|
||||
|
||||
560
lib/pages/service/multi_video_player.dart
Normal file
560
lib/pages/service/multi_video_player.dart
Normal file
@ -0,0 +1,560 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
class NhanceMultiVideoWrapper extends StatefulWidget {
|
||||
final List<String> videoUrls;
|
||||
|
||||
const NhanceMultiVideoWrapper({
|
||||
super.key,
|
||||
required this.videoUrls,
|
||||
});
|
||||
|
||||
@override
|
||||
State<NhanceMultiVideoWrapper> createState() =>
|
||||
_NhanceMultiVideoWrapperState();
|
||||
}
|
||||
|
||||
class _NhanceMultiVideoWrapperState extends State<NhanceMultiVideoWrapper> {
|
||||
final PageController _pageController = PageController();
|
||||
final List<VideoPlayerController> _controllers = [];
|
||||
|
||||
int _currentIndex = 0;
|
||||
bool _isMuted = false;
|
||||
|
||||
/// Optional static cover image
|
||||
final String coverImage =
|
||||
"https://picsum.photos/900/500"; // replace with API thumbnail if available
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initControllers();
|
||||
}
|
||||
|
||||
Future<void> _initControllers() async {
|
||||
for (final url in widget.videoUrls) {
|
||||
final controller = VideoPlayerController.networkUrl(Uri.parse(url));
|
||||
await controller.initialize();
|
||||
controller.setLooping(true);
|
||||
controller.setVolume(1);
|
||||
_controllers.add(controller);
|
||||
}
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
for (final c in _controllers) {
|
||||
c.dispose();
|
||||
}
|
||||
_pageController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _next() {
|
||||
if (_currentIndex < _controllers.length - 1) {
|
||||
_pageController.nextPage(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _previous() {
|
||||
if (_currentIndex > 0) {
|
||||
_pageController.previousPage(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _seek(VideoPlayerController c, int seconds) {
|
||||
final pos = c.value.position + Duration(seconds: seconds);
|
||||
c.seekTo(
|
||||
pos < Duration.zero ? Duration.zero : pos,
|
||||
);
|
||||
}
|
||||
|
||||
void _toggleMute(VideoPlayerController c) {
|
||||
setState(() {
|
||||
_isMuted = !_isMuted;
|
||||
c.setVolume(_isMuted ? 0 : 1);
|
||||
});
|
||||
}
|
||||
|
||||
void _openFullscreen(VideoPlayerController controller) {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
body: Center(
|
||||
child: AspectRatio(
|
||||
aspectRatio: controller.value.aspectRatio,
|
||||
child: VideoPlayer(controller),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_controllers.isEmpty) {
|
||||
return const SizedBox(
|
||||
height: 250,
|
||||
child: Center(child: CircularProgressIndicator()),
|
||||
);
|
||||
}
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// const Text(
|
||||
// "Videos",
|
||||
// style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
|
||||
// ),
|
||||
// const SizedBox(height: 10),
|
||||
|
||||
SizedBox(
|
||||
height: kIsWeb ? 340 : 240,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
PageView.builder(
|
||||
controller: _pageController,
|
||||
itemCount: _controllers.length,
|
||||
onPageChanged: (index) {
|
||||
setState(() {
|
||||
_controllers[_currentIndex].pause();
|
||||
_currentIndex = index;
|
||||
});
|
||||
},
|
||||
itemBuilder: (context, index) {
|
||||
final controller = _controllers[index];
|
||||
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
width: kIsWeb ? 520 : double.infinity,
|
||||
child: AspectRatio(
|
||||
aspectRatio: controller.value.aspectRatio,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
/// 🎥 VIDEO
|
||||
VideoPlayer(controller),
|
||||
|
||||
/// 🖼 COVER IMAGE
|
||||
if (!controller.value.isPlaying)
|
||||
// Positioned.fill(
|
||||
// child: Image.network(
|
||||
// coverImage,
|
||||
// fit: BoxFit.cover,
|
||||
// ),
|
||||
// ),
|
||||
|
||||
/// ▶ PLAY / PAUSE
|
||||
IconButton(
|
||||
iconSize: 64,
|
||||
icon: Icon(
|
||||
controller.value.isPlaying
|
||||
? Icons.pause_circle
|
||||
: Icons.play_circle,
|
||||
color: Colors.white,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
controller.value.isPlaying
|
||||
? controller.pause()
|
||||
: controller.play();
|
||||
});
|
||||
},
|
||||
),
|
||||
|
||||
/// 🎛 BOTTOM CONTROLS
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10, vertical: 6),
|
||||
color: Colors.black.withOpacity(0.55),
|
||||
child: Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.replay_10,
|
||||
color: Colors.white),
|
||||
onPressed: () =>
|
||||
_seek(controller, -10),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.forward_10,
|
||||
color: Colors.white),
|
||||
onPressed: () =>
|
||||
_seek(controller, 10),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
_isMuted
|
||||
? Icons.volume_off
|
||||
: Icons.volume_up,
|
||||
color: Colors.white,
|
||||
),
|
||||
onPressed: () =>
|
||||
_toggleMute(controller),
|
||||
),
|
||||
Expanded(
|
||||
child: VideoProgressIndicator(
|
||||
controller,
|
||||
allowScrubbing: true,
|
||||
colors: VideoProgressColors(
|
||||
playedColor:
|
||||
const Color(0xFF593AFF),
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.fullscreen,
|
||||
color: Colors.white),
|
||||
onPressed: () =>
|
||||
_openFullscreen(controller),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
/// ⬅ PREVIOUS (WEB)
|
||||
if (kIsWeb)
|
||||
Positioned(
|
||||
left: 12,
|
||||
child: _ArrowButton(
|
||||
icon: Icons.chevron_left,
|
||||
enabled: _currentIndex > 0,
|
||||
onTap: _previous,
|
||||
),
|
||||
),
|
||||
|
||||
/// ➡ NEXT (WEB)
|
||||
if (kIsWeb)
|
||||
Positioned(
|
||||
right: 12,
|
||||
child: _ArrowButton(
|
||||
icon: Icons.chevron_right,
|
||||
enabled: _currentIndex < _controllers.length - 1,
|
||||
onTap: _next,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 8),
|
||||
|
||||
/// 🔘 DOT INDICATOR
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: List.generate(
|
||||
_controllers.length,
|
||||
(i) => AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 250),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 4),
|
||||
height: 8,
|
||||
width: _currentIndex == i ? 20 : 8,
|
||||
decoration: BoxDecoration(
|
||||
color: _currentIndex == i
|
||||
? const Color(0xFF593AFF)
|
||||
: Colors.grey.shade400,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 🔘 Arrow Button
|
||||
class _ArrowButton extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final bool enabled;
|
||||
final VoidCallback onTap;
|
||||
|
||||
const _ArrowButton({
|
||||
required this.icon,
|
||||
required this.enabled,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return IgnorePointer(
|
||||
ignoring: !enabled,
|
||||
child: AnimatedOpacity(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
opacity: enabled ? 1 : 0.3,
|
||||
child: Material(
|
||||
color: Colors.black.withOpacity(0.6),
|
||||
shape: const CircleBorder(),
|
||||
elevation: 4,
|
||||
child: InkWell(
|
||||
customBorder: const CircleBorder(),
|
||||
onTap: onTap,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Icon(icon, size: 36, color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// import 'package:flutter/foundation.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:video_player/video_player.dart';
|
||||
//
|
||||
// class NhanceMultiVideoWrapper extends StatefulWidget {
|
||||
// final List<String> videoUrls;
|
||||
//
|
||||
// const NhanceMultiVideoWrapper({
|
||||
// super.key,
|
||||
// required this.videoUrls,
|
||||
// });
|
||||
//
|
||||
// @override
|
||||
// State<NhanceMultiVideoWrapper> createState() =>
|
||||
// _NhanceMultiVideoWrapperState();
|
||||
// }
|
||||
//
|
||||
// class _NhanceMultiVideoWrapperState extends State<NhanceMultiVideoWrapper> {
|
||||
// final PageController _pageController = PageController();
|
||||
// int _currentIndex = 0;
|
||||
//
|
||||
// final List<VideoPlayerController> _controllers = [];
|
||||
//
|
||||
// @override
|
||||
// void initState() {
|
||||
// super.initState();
|
||||
// _initControllers();
|
||||
// }
|
||||
//
|
||||
// Future<void> _initControllers() async {
|
||||
// for (final url in widget.videoUrls) {
|
||||
// final controller = VideoPlayerController.networkUrl(Uri.parse(url));
|
||||
// await controller.initialize();
|
||||
// controller.setLooping(true);
|
||||
// controller.setVolume(1);
|
||||
// _controllers.add(controller);
|
||||
// }
|
||||
//
|
||||
// if (mounted) setState(() {});
|
||||
// }
|
||||
//
|
||||
// @override
|
||||
// void dispose() {
|
||||
// for (final c in _controllers) {
|
||||
// c.dispose();
|
||||
// }
|
||||
// _pageController.dispose();
|
||||
// super.dispose();
|
||||
// }
|
||||
//
|
||||
// void _next() {
|
||||
// if (_currentIndex < widget.videoUrls.length - 1) {
|
||||
// _pageController.nextPage(
|
||||
// duration: const Duration(milliseconds: 300),
|
||||
// curve: Curves.easeInOut,
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// void _previous() {
|
||||
// if (_currentIndex > 0) {
|
||||
// _pageController.previousPage(
|
||||
// duration: const Duration(milliseconds: 300),
|
||||
// curve: Curves.easeInOut,
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// if (_controllers.isEmpty) {
|
||||
// return const SizedBox(
|
||||
// height: 250,
|
||||
// child: Center(child: CircularProgressIndicator()),
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// return Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// // Text(
|
||||
// // "Videos",
|
||||
// // style: Theme.of(context)
|
||||
// // .textTheme
|
||||
// // .titleMedium
|
||||
// // ?.copyWith(fontWeight: FontWeight.w600),
|
||||
// // ),
|
||||
// // const SizedBox(height: 10),
|
||||
//
|
||||
// /// 🎥 VIDEO WITH WEB CONTROLS
|
||||
// SizedBox(
|
||||
// height: kIsWeb ? 320 : 220,
|
||||
// child: Stack(
|
||||
// alignment: Alignment.center,
|
||||
// children: [
|
||||
// PageView.builder(
|
||||
// controller: _pageController,
|
||||
// itemCount: _controllers.length,
|
||||
// onPageChanged: (index) {
|
||||
// setState(() {
|
||||
// _controllers[_currentIndex].pause();
|
||||
// _currentIndex = index;
|
||||
// });
|
||||
// },
|
||||
// itemBuilder: (context, index) {
|
||||
// final controller = _controllers[index];
|
||||
//
|
||||
// return Center(
|
||||
// child: SizedBox(
|
||||
// width: kIsWeb ? 520 : double.infinity,
|
||||
// child: AspectRatio(
|
||||
// aspectRatio: controller.value.aspectRatio,
|
||||
// child: Stack(
|
||||
// alignment: Alignment.center,
|
||||
// children: [
|
||||
// VideoPlayer(controller),
|
||||
//
|
||||
// IconButton(
|
||||
// iconSize: 60,
|
||||
// icon: Icon(
|
||||
// controller.value.isPlaying
|
||||
// ? Icons.pause_circle
|
||||
// : Icons.play_circle,
|
||||
// color: Colors.white,
|
||||
// ),
|
||||
// onPressed: () {
|
||||
// setState(() {
|
||||
// controller.value.isPlaying
|
||||
// ? controller.pause()
|
||||
// : controller.play();
|
||||
// });
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
//
|
||||
// /// ⬅ PREVIOUS (WEB ONLY)
|
||||
// if (kIsWeb)
|
||||
// Positioned(
|
||||
// left: 16,
|
||||
// child: _VideoArrowButton(
|
||||
// icon: Icons.chevron_left,
|
||||
// enabled: _currentIndex > 0,
|
||||
// onTap: _previous,
|
||||
// ),
|
||||
// ),
|
||||
//
|
||||
//
|
||||
// /// ➡ NEXT (WEB ONLY)
|
||||
// if (kIsWeb)
|
||||
// Positioned(
|
||||
// right: 16,
|
||||
// child: _VideoArrowButton(
|
||||
// icon: Icons.chevron_right,
|
||||
// enabled: _currentIndex < _controllers.length - 1,
|
||||
// onTap: _next,
|
||||
// ),
|
||||
// ),
|
||||
//
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
//
|
||||
// const SizedBox(height: 8),
|
||||
//
|
||||
// /// 🔘 DOT INDICATOR
|
||||
// Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: List.generate(
|
||||
// _controllers.length,
|
||||
// (index) => AnimatedContainer(
|
||||
// duration: const Duration(milliseconds: 250),
|
||||
// margin: const EdgeInsets.symmetric(horizontal: 4),
|
||||
// height: 8,
|
||||
// width: _currentIndex == index ? 20 : 8,
|
||||
// decoration: BoxDecoration(
|
||||
// color: _currentIndex == index
|
||||
// ? const Color(0xFF593AFF)
|
||||
// : Colors.grey.shade400,
|
||||
// borderRadius: BorderRadius.circular(4),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
//
|
||||
//
|
||||
// }
|
||||
// class _VideoArrowButton extends StatelessWidget {
|
||||
// final IconData icon;
|
||||
// final bool enabled;
|
||||
// final VoidCallback onTap;
|
||||
//
|
||||
// const _VideoArrowButton({
|
||||
// required this.icon,
|
||||
// required this.enabled,
|
||||
// required this.onTap,
|
||||
// });
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return IgnorePointer(
|
||||
// ignoring: !enabled,
|
||||
// child: AnimatedOpacity(
|
||||
// duration: const Duration(milliseconds: 200),
|
||||
// opacity: enabled ? 1 : 0.3,
|
||||
// child: Material(
|
||||
// color: Colors.black.withOpacity(0.55),
|
||||
// shape: const CircleBorder(),
|
||||
// elevation: 4,
|
||||
// child: InkWell(
|
||||
// customBorder: const CircleBorder(),
|
||||
// onTap: onTap,
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.all(10),
|
||||
// child: Icon(
|
||||
// icon,
|
||||
// size: 36,
|
||||
// color: Colors.white,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
81
lib/pages/service/network_video_player.dart
Normal file
81
lib/pages/service/network_video_player.dart
Normal file
@ -0,0 +1,81 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
class NetworkVideoPlayer extends StatefulWidget {
|
||||
final String videoUrl;
|
||||
|
||||
const NetworkVideoPlayer({
|
||||
super.key,
|
||||
required this.videoUrl,
|
||||
});
|
||||
|
||||
@override
|
||||
State<NetworkVideoPlayer> createState() => _NetworkVideoPlayerState();
|
||||
}
|
||||
|
||||
class _NetworkVideoPlayerState extends State<NetworkVideoPlayer> {
|
||||
late VideoPlayerController _controller;
|
||||
bool isInitialized = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_controller = VideoPlayerController.networkUrl(
|
||||
Uri.parse(widget.videoUrl),
|
||||
)..initialize().then((_) {
|
||||
if (!mounted) return;
|
||||
setState(() => isInitialized = true);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AspectRatio(
|
||||
aspectRatio: isInitialized
|
||||
? _controller.value.aspectRatio
|
||||
: 16 / 9,
|
||||
child: isInitialized
|
||||
? Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
VideoPlayer(_controller),
|
||||
|
||||
IconButton(
|
||||
iconSize: 60,
|
||||
icon: Icon(
|
||||
_controller.value.isPlaying
|
||||
? Icons.pause_circle
|
||||
: Icons.play_circle,
|
||||
color: Colors.white,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_controller.value.isPlaying
|
||||
? _controller.pause()
|
||||
: _controller.play();
|
||||
});
|
||||
},
|
||||
),
|
||||
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: VideoProgressIndicator(
|
||||
_controller,
|
||||
allowScrubbing: true,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: const Center(child: CircularProgressIndicator()),
|
||||
);
|
||||
}
|
||||
}
|
||||
74
lib/pages/service/video_player_widget.dart
Normal file
74
lib/pages/service/video_player_widget.dart
Normal file
@ -0,0 +1,74 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
class NetworkVideoPlayer extends StatefulWidget {
|
||||
final String videoUrl;
|
||||
|
||||
const NetworkVideoPlayer({super.key, required this.videoUrl});
|
||||
|
||||
@override
|
||||
State<NetworkVideoPlayer> createState() => _NetworkVideoPlayerState();
|
||||
}
|
||||
|
||||
class _NetworkVideoPlayerState extends State<NetworkVideoPlayer> {
|
||||
late VideoPlayerController _controller;
|
||||
bool isInitialized = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_controller = VideoPlayerController.networkUrl(
|
||||
Uri.parse(widget.videoUrl),
|
||||
)
|
||||
..initialize().then((_) {
|
||||
setState(() {
|
||||
isInitialized = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AspectRatio(
|
||||
aspectRatio:
|
||||
isInitialized ? _controller.value.aspectRatio : 16 / 9,
|
||||
child: isInitialized
|
||||
? Stack(
|
||||
alignment: Alignment.bottomCenter,
|
||||
children: [
|
||||
VideoPlayer(_controller),
|
||||
VideoProgressIndicator(
|
||||
_controller,
|
||||
allowScrubbing: true,
|
||||
),
|
||||
Center(
|
||||
child: IconButton(
|
||||
iconSize: 60,
|
||||
icon: Icon(
|
||||
_controller.value.isPlaying
|
||||
? Icons.pause_circle
|
||||
: Icons.play_circle,
|
||||
color: Colors.white,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_controller.value.isPlaying
|
||||
? _controller.pause()
|
||||
: _controller.play();
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: const Center(child: CircularProgressIndicator()),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -72,6 +72,8 @@ dependencies:
|
||||
carousel_slider: ^5.1.1
|
||||
dropdown_search: ^6.0.2
|
||||
ribbon_widget: ^1.0.5
|
||||
flutter_html: ^3.0.0
|
||||
video_player: ^2.10.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
@ -98,7 +100,6 @@ flutter:
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
assets:
|
||||
- assets/
|
||||
- assets/botman_chat.html
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user