Flutter Workgroup Meetings
Mohamad's interest is in Programming (Mobile, Web, Database and Machine Learning). He is studying at the Center For Artificial Intelligence Technology (CAIT), Universiti Kebangsaan Malaysia (UKM).
Prep WorkgroupMeetings API
Laravel api/WorkgroupMeetingController.php:
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Workgroup;
use App\Models\WorkgroupMeeting;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class WorkgroupMeetingController extends Controller
{
public function index($workgroupId)
{
$user = auth()->user();
$workgroups = Workgroup::where('mngr_email', $user->email)
->get();
// Check if the requested workgroup belongs to the authenticated user
$requestedWorkgroup = $workgroups->firstWhere('id', $workgroupId);
if (!$requestedWorkgroup) {
return response()->json(['error' => 'Unauthorized'], 403);
}
$workgroupMeetings = WorkgroupMeeting::where('workgroup_id', $workgroupId)->get();
return response()->json($workgroupMeetings);
}
}
Laravel routes/api.php:
...
use App\Http\Controllers\Api\WorkgroupMeetingController;
Route::middleware('auth:sanctum')->group(function () {
Route::get('workgroup/{workgroup_id}/meeting', [WorkgroupMeetingController::class, 'index']);
});
...
Edit WorkgroupMeetings Screen
File-> lib/modules/workgroupmeetings.dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:hive/hive.dart';
class WorkgroupMeetingsScreen extends StatefulWidget {
final dynamic workgroup;
const WorkgroupMeetingsScreen({Key? key, required this.workgroup})
: super(key: key);
@override
_WorkgroupMeetingsScreenState createState() =>
_WorkgroupMeetingsScreenState();
}
class _WorkgroupMeetingsScreenState extends State<WorkgroupMeetingsScreen> {
List<dynamic> _meetings = [];
final Box _boxUser = Hive.box("user");
bool _isLoading = true;
@override
void initState() {
super.initState();
_fetchMeetings();
}
Future<void> _fetchMeetings() async {
final url =
'https://demo.razzi.my/spotnet/public/api/workgroup/${widget.workgroup['id']}/meeting';
final authToken = await _boxUser.get('user_token');
try {
final response = await http.get(
Uri.parse(url),
headers: {
'Authorization': 'Bearer $authToken',
'Content-Type': 'application/json',
},
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body) as List;
setState(() {
_meetings = data;
_isLoading = false;
});
} else {
setState(() {
_isLoading = false;
});
// Handle error
print('Error fetching meetings: ${response.statusCode}');
}
} catch (e) {
setState(() {
_isLoading = false;
});
// Handle network error
print('Error fetching meetings: $e');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Meetings for ${widget.workgroup['name']}'),
actions: [
ElevatedButton(
onPressed: () {
// Navigate to the "Create Meeting" screen
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => CreateMeetingScreen(
workgroup: widget.workgroup,
))).then((_) {
// This block runs when you have come back to the 1st Page from 2nd.
_fetchMeetings();
});
},
style: ElevatedButton.styleFrom(
backgroundColor: Color.fromARGB(
255, 122, 192, 245), // Set the background color to red
foregroundColor: Colors.white, // Set the font color to white
padding:
const EdgeInsets.all(8.0), // Adjust the padding as needed
),
child: const Icon(Icons.add),
),
],
),
body: _isLoading
? const Center(child: CircularProgressIndicator())
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: ListView.builder(
itemCount: _meetings.length,
itemBuilder: (context, index) {
final meeting = _meetings[index];
return ListTile(
leading: IconButton(
icon: const Icon(Icons.info),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) =>
MeetingDetailsScreen(meeting: meeting)))
.then((_) {
// This block runs when you have come back to the 1st Page from 2nd.
_fetchMeetings();
});
},
),
title: Text('Description: ${meeting['desn']}'),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Reference: ${meeting['refn']}'),
Text('Location: ${meeting['locn']}'),
Text(
'Start Date: ${DateTime.fromMillisecondsSinceEpoch(meeting['estartdate'] * 1000).toString()}'),
Text('Attendees: ${meeting['attd']}'),
],
),
trailing: IconButton(
icon: const Icon(Icons.chevron_right),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) =>
MeetingDetailsScreen(meeting: meeting),
),
).then((_) {
// This block runs when you have come back to the 1st Page from 2nd.
_fetchMeetings();
});
},
),
);
},
),
),
);
}
}
class CreateMeetingScreen extends StatefulWidget {
final dynamic workgroup;
const CreateMeetingScreen({Key? key, required this.workgroup})
: super(key: key);
@override
_CreateMeetingScreenState createState() => _CreateMeetingScreenState();
}
class _CreateMeetingScreenState extends State<CreateMeetingScreen> {
// Implement the logic for creating a new meeting
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
class MeetingDetailsScreen extends StatefulWidget {
final dynamic meeting;
const MeetingDetailsScreen({Key? key, required this.meeting})
: super(key: key);
@override
_MeetingDetailsScreenState createState() => _MeetingDetailsScreenState();
}
class _MeetingDetailsScreenState extends State<MeetingDetailsScreen> {
// Implement the logic for displaying the details of a meeting
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
At the moment MeetingDetailsScreen and CreateMeetingScreen are just stub classes.