新增icon,新增活動詳情

This commit is contained in:
jasonchenwork 2025-06-03 17:50:50 +08:00
parent 30c2c953a1
commit 4eb4fb918b
12 changed files with 198 additions and 71 deletions

BIN
assets/icons/community.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 B

BIN
assets/icons/emergency.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 B

BIN
assets/icons/package.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 B

BIN
assets/icons/payment.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 845 B

BIN
assets/icons/repair.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

BIN
assets/icons/visitor.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 876 B

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'activity_submit.dart';
import 'activity_detail.dart';
class ActivityListPage extends StatefulWidget {
const ActivityListPage({Key? key}) : super(key: key);
@ -94,7 +95,14 @@ class _ActivityListPageState extends State<ActivityListPage> {
children: [
TextButton(
onPressed: () {
// TODO :
Navigator.push(
context,
MaterialPageRoute(
builder:
(context) =>
const ActivityDetailPage(activityId: 2),
),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.transparent,

160
lib/activity_detail.dart Normal file
View File

@ -0,0 +1,160 @@
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class ActivityDetailPage extends StatefulWidget {
final int activityId;
const ActivityDetailPage({super.key, required this.activityId});
@override
State<ActivityDetailPage> createState() => _ActivityDetailPageState();
}
class _ActivityDetailPageState extends State<ActivityDetailPage> {
Map<String, dynamic>? activity;
bool isLoading = true;
// false API
final bool useMockData = true;
@override
void initState() {
super.initState();
fetchActivityDetail();
}
Future<void> fetchActivityDetail() async {
if (useMockData) {
//await Future.delayed(const Duration(seconds: 1)); //
setState(() {
activity = {
"title": "🎉 社區春季市集",
"time": "2025/04/2710:00 - 16:00",
"location": "中庭花園",
"desc": "市集將有手作小物、美食攤販及親子遊戲活動,歡迎全體住戶參與!",
"image": "https://picsum.photos/id/1011/600/300",
"canRegister": true,
};
isLoading = false;
});
} else {
try {
final response = await http.get(
// API
Uri.parse('https://your-api.com/activities/${widget.activityId}'),
);
if (response.statusCode == 200) {
setState(() {
activity = json.decode(response.body);
isLoading = false;
});
} else {
throw Exception('Failed to load activity');
}
} catch (e) {
setState(() {
isLoading = false;
activity = null;
});
}
}
}
void _showRegisterDialog() {
int peopleCount = 1;
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('確認報名'),
content: DropdownButtonFormField<int>(
value: peopleCount,
items:
[1, 2, 3, 4]
.map((e) => DropdownMenuItem(value: e, child: Text('$e')))
.toList(),
onChanged: (value) {
if (value != null) peopleCount = value;
},
decoration: const InputDecoration(labelText: '選擇報名人數'),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'已報名活動 ID ${widget.activityId},人數:$peopleCount',
),
),
);
},
child: const Text('確認報名'),
),
],
);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('活動詳情')),
body:
isLoading
? const Center(child: CircularProgressIndicator())
: activity == null
? const Center(child: Text('找不到該活動。'))
: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.network(
activity!['image'],
fit: BoxFit.cover,
),
),
const SizedBox(height: 16),
Text(
activity!['title'],
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
'時間:${activity!['time']}',
style: const TextStyle(color: Colors.grey),
),
const SizedBox(height: 4),
Text(
'地點:${activity!['location']}',
style: const TextStyle(color: Colors.grey),
),
const SizedBox(height: 12),
Text(activity!['desc']),
const SizedBox(height: 24),
if (activity!['canRegister'] == true)
Center(
child: ElevatedButton(
onPressed: _showRegisterDialog,
child: const Text('我要報名'),
),
),
],
),
),
);
}
}

View File

@ -50,7 +50,10 @@ class _FeedbackPageState extends State<FeedbackPage> {
.map(
(value) => DropdownMenuItem(
value: value,
child: Text(value),
child: Text(
value,
style: TextStyle(fontSize: 14),
),
),
)
.toList(),
@ -68,7 +71,10 @@ class _FeedbackPageState extends State<FeedbackPage> {
.map(
(value) => DropdownMenuItem(
value: value,
child: Text(value),
child: Text(
value,
style: TextStyle(fontSize: 14),
),
),
)
.toList(),
@ -84,6 +90,7 @@ class _FeedbackPageState extends State<FeedbackPage> {
border: OutlineInputBorder(),
floatingLabelBehavior: FloatingLabelBehavior.always,
),
style: TextStyle(fontSize: 13),
),
const SizedBox(height: 24),
Row(

View File

@ -94,36 +94,6 @@ class HomeContentPage extends StatelessWidget {
),
const SizedBox(width: 8),
NotificationIcon(),
/*Stack(
children: [
IconButton(
icon: const Icon(Icons.notifications),
onPressed: () {
// TODO:
},
),
Positioned(
right: 8,
top: 8,
child: Container(
padding: const EdgeInsets.all(2),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(10),
),
constraints: const BoxConstraints(
minWidth: 16,
minHeight: 16,
),
child: const Text(
'3',
style: TextStyle(color: Colors.white, fontSize: 10),
textAlign: TextAlign.center,
),
),
),
],
),*/
],
),
],
@ -219,7 +189,7 @@ class HomeContentPage extends StatelessWidget {
childAspectRatio: 1,
children: [
_buildQuickButton(context, '報修', 'assets/icons/repair.png'),
_buildQuickButton(context, '包裹', 'assets/icons/mail.png'),
_buildQuickButton(context, '包裹', 'assets/icons/package.png'),
_buildQuickButton(context, '訪客', 'assets/icons/visitor.png'),
_buildQuickButton(context, '繳費', 'assets/icons/payment.png'),
_buildQuickButton(context, '社區互動', 'assets/icons/community.png'),

View File

@ -21,30 +21,6 @@ class PersonalPage extends StatelessWidget {
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
centerTitle: true,
leading: IconButton(
icon: Image.asset('assets/icons/back.png', width: 24, height: 24),
onPressed: () {
Navigator.of(context).pop();
},
),
actions: [
IconButton(
icon: Image.asset(
'assets/icons/notification.png',
width: 24,
height: 24,
),
onPressed: () {
// TODO:
},
),
IconButton(
icon: Image.asset('assets/icons/qr.png', width: 24, height: 24),
onPressed: () {
// TODO: QR碼按鈕事件
},
),
],
),
body: ListView(
padding: const EdgeInsets.all(16),
@ -139,19 +115,24 @@ class PersonalPage extends StatelessWidget {
),
child: Stack(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text('社區地址:新北市板橋區幸福路 88 號', style: TextStyle(fontSize: 14)),
SizedBox(height: 6),
Text('管理室電話02-2233-4455', style: TextStyle(fontSize: 14)),
SizedBox(height: 6),
Text(
'管委會 Emailservice@garden-community.tw',
style: TextStyle(fontSize: 14),
),
],
//
Padding(
padding: const EdgeInsets.only(right: 120), //
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text('社區地址:新北市板橋區幸福路 88 號', style: TextStyle(fontSize: 12)),
SizedBox(height: 6),
Text('管理室電話02-2233-4455', style: TextStyle(fontSize: 12)),
SizedBox(height: 6),
Text(
'管委會 Emailservice@garden-community.tw',
style: TextStyle(fontSize: 12),
),
],
),
),
//
Positioned(
top: 0,
right: 0,
@ -194,7 +175,7 @@ class PersonalPage extends StatelessWidget {
mainAxisSpacing: 12,
physics: const NeverScrollableScrollPhysics(),
children: [
_buildMenuItem('繳費通知', 'receipt.png', () {
_buildMenuItem('繳費通知', 'payment.png', () {
showModalBottomSheet(
context: context,
isScrollControlled: true,

View File

@ -54,6 +54,7 @@ dev_dependencies:
flutter:
assets:
- assets/images/
- assets/icons/
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.