feat: integrate api latihan & menambahkan base_url
This commit is contained in:
parent
363d216f21
commit
76710e8216
|
|
@ -1,4 +1,4 @@
|
||||||
// lib/config.dart
|
// lib/config.dart
|
||||||
class BaseUrl {
|
class BaseUrl {
|
||||||
static const String baseUrl = 'http://192.168.100.45:8000/api';
|
static const String baseUrl = 'http://192.168.100.13:8000/api';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ final router = GoRouter(
|
||||||
builder: (BuildContext context, GoRouterState state) {
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
final Map<String, dynamic> extra = state.extra as Map<String, dynamic>;
|
final Map<String, dynamic> extra = state.extra as Map<String, dynamic>;
|
||||||
return SubMateriPage(
|
return SubMateriPage(
|
||||||
|
id: extra['id'],
|
||||||
title: extra['title'],
|
title: extra['title'],
|
||||||
description: extra['description'],
|
description: extra['description'],
|
||||||
videoLink: extra['videoLink'],
|
videoLink: extra['videoLink'],
|
||||||
|
|
@ -52,33 +53,34 @@ final router = GoRouter(
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
// Rute untuk halaman Latihan
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/latihan',
|
path: '/latihan',
|
||||||
builder: (context, state) => LatihanPage(),
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
),
|
|
||||||
// GoRoute(
|
|
||||||
// path: '/pelafalan',
|
|
||||||
// builder: (context, state) {
|
|
||||||
// final Map<String, dynamic> extra = state.extra as Map<String, dynamic>;
|
|
||||||
// return PelafalanPage(
|
|
||||||
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
GoRoute(
|
|
||||||
path: '/pelafalan',
|
|
||||||
builder: (context, state) {
|
|
||||||
// Ambil data dari state.extra yang dikirim melalui context.go()
|
|
||||||
final Map<String, dynamic> extra = state.extra as Map<String, dynamic>;
|
final Map<String, dynamic> extra = state.extra as Map<String, dynamic>;
|
||||||
final int currentStep = extra['currentStep']; // Ambil currentStep
|
return LatihanPage(
|
||||||
|
id: extra['id'],
|
||||||
// Kirim data ke PelafalanPage
|
currentStep: extra['currentStep'] ?? 0, // Menambahkan parameter currentStep
|
||||||
return PelafalanPage(
|
|
||||||
currentStep:
|
|
||||||
currentStep, // Mengirim currentStep ke halaman PelafalanPage
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// Rute untuk halaman Pelafalan
|
||||||
|
GoRoute(
|
||||||
|
path: '/pelafalan',
|
||||||
|
builder: (context, state) {
|
||||||
|
final Map<String, dynamic> extra = state.extra as Map<String, dynamic>;
|
||||||
|
final int currentStep = extra['currentStep'];
|
||||||
|
final List<dynamic> latihanData = extra['latihanData'];
|
||||||
|
|
||||||
|
return PelafalanPage(
|
||||||
|
id: extra['id'],
|
||||||
|
currentStep: currentStep,
|
||||||
|
latihanData: latihanData,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/kemajuan',
|
path: '/kemajuan',
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ double spacing = 8;
|
||||||
|
|
||||||
Color primaryColor = const Color(0xffFE0E46);
|
Color primaryColor = const Color(0xffFE0E46);
|
||||||
Color secondPrimaryColor = const Color(0xff08CACA);
|
Color secondPrimaryColor = const Color(0xff08CACA);
|
||||||
|
Color secondPrimaryColor1 = const Color.fromARGB(255, 189, 211, 211);
|
||||||
Color thirdPrimaryColor = const Color(0xffF2C98A);
|
Color thirdPrimaryColor = const Color(0xffF2C98A);
|
||||||
Color blueBorderChat = const Color(0xffE0F6CA);
|
Color blueBorderChat = const Color(0xffE0F6CA);
|
||||||
Color primaryColorW = const Color(0xff003299).withOpacity(0.2);
|
Color primaryColorW = const Color(0xff003299).withOpacity(0.2);
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,98 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:flame_audio/flame_audio.dart'; // Import Flame Audio
|
import 'package:ta_tahsin/core/baseurl/base_url.dart';
|
||||||
import 'package:ta_tahsin/core/theme.dart';
|
import 'package:ta_tahsin/core/theme.dart';
|
||||||
import '../materi/model/model_data_materi.dart';
|
import 'package:http/http.dart' as http;
|
||||||
import 'model/model_data_latihan.dart';
|
import 'dart:convert'; // Untuk parsing JSON
|
||||||
|
import 'dart:async'; // Untuk timer
|
||||||
|
import 'package:audioplayers/audioplayers.dart'; // Tambahkan audioplayers
|
||||||
|
|
||||||
class LatihanPage extends StatefulWidget {
|
class LatihanPage extends StatefulWidget {
|
||||||
const LatihanPage({super.key});
|
final int id; // ID yang diteruskan dari halaman sebelumnya
|
||||||
|
final int currentStep; // Menambahkan parameter currentStep
|
||||||
|
|
||||||
|
const LatihanPage({super.key, required this.id, required this.currentStep});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_LatihanPageState createState() => _LatihanPageState();
|
_LatihanPageState createState() => _LatihanPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LatihanPageState extends State<LatihanPage> {
|
class _LatihanPageState extends State<LatihanPage> {
|
||||||
int currentStep = 0; // Start with the first step
|
late Future<List<dynamic>> latihanData; // Data latihan berdasarkan id_submateri
|
||||||
|
bool isRecording = false; // Menandakan apakah sedang merekam
|
||||||
|
int timer = 10; // Timer dimulai dari 10 detik
|
||||||
|
late Timer countdownTimer; // Timer untuk countdown
|
||||||
|
String timerText = "10"; // Menampilkan countdown timer dalam teks
|
||||||
|
final AudioPlayer _audioPlayer = AudioPlayer(); // Inisialisasi Audioplayer
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
latihanData = fetchLatihanData(widget.id); // Ambil data latihan berdasarkan id_submateri yang diteruskan
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fungsi untuk mengambil data latihan dari API
|
||||||
|
Future<List<dynamic>> fetchLatihanData(int id_submateri) async {
|
||||||
|
final response = await http.get(
|
||||||
|
Uri.parse('${BaseUrl.baseUrl}/latihan/$id_submateri'), // Gantilah dengan URL API yang sesuai
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
return json.decode(response.body)['data']; // Parse data latihan dari API
|
||||||
|
} else {
|
||||||
|
throw Exception('Failed to load latihan');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fungsi untuk mulai countdown timer
|
||||||
|
void startTimer() {
|
||||||
|
setState(() {
|
||||||
|
isRecording = true; // Set to true when starting to record
|
||||||
|
timer = 10; // Reset timer to 10 seconds
|
||||||
|
timerText = timer.toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
countdownTimer = Timer.periodic(Duration(seconds: 1), (timer) {
|
||||||
|
if (this.timer > 0) {
|
||||||
|
setState(() {
|
||||||
|
this.timer--;
|
||||||
|
timerText = this.timer.toString();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
countdownTimer.cancel();
|
||||||
|
setState(() {
|
||||||
|
isRecording = false; // Stop recording after timer ends
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fungsi untuk menghentikan timer dan reset ke kondisi semula
|
||||||
|
void stopTimer() {
|
||||||
|
countdownTimer.cancel();
|
||||||
|
setState(() {
|
||||||
|
isRecording = false; // Stop recording
|
||||||
|
timer = 10; // Reset timer
|
||||||
|
timerText = timer.toString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fungsi untuk memutar audio dari asset berdasarkan URL yang diterima
|
||||||
|
void playAudio(String audioUrl) async {
|
||||||
|
// Menggunakan AssetSource untuk memutar audio dari asset
|
||||||
|
await _audioPlayer.play(AssetSource(audioUrl)); // Gunakan AssetSource untuk audio lokal
|
||||||
|
print("Audio playing...");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (currentStep >= latihanList.length) {
|
|
||||||
currentStep = latihanList.length - 1; // Stay at last step if out of bounds
|
|
||||||
}
|
|
||||||
|
|
||||||
final arabicText = latihanList[currentStep]['arabicText'];
|
|
||||||
final latinText = latihanList[currentStep]['latinText'];
|
|
||||||
final audioFile = latihanList[currentStep]['audioFile']; // Get the audio file from data
|
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: const Icon(Icons.close),
|
icon: const Icon(Icons.close),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.go('/materi', extra: {
|
context.go('/navigasi'); // Navigasi kembali ke halaman utama
|
||||||
'title': 'Makharijul Huruf',
|
|
||||||
'description': 'tempat keluarnya huruf',
|
|
||||||
'subMateri': materiList[0]['subMateri'],
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
|
|
@ -42,12 +100,12 @@ class _LatihanPageState extends State<LatihanPage> {
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: List.generate(4, (index) {
|
children: List.generate(3, (index) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||||
child: CircleAvatar(
|
child: CircleAvatar(
|
||||||
radius: 12,
|
radius: 12,
|
||||||
backgroundColor: index <= currentStep
|
backgroundColor: index <= widget.currentStep
|
||||||
? secondPrimaryColor
|
? secondPrimaryColor
|
||||||
: Colors.grey,
|
: Colors.grey,
|
||||||
),
|
),
|
||||||
|
|
@ -58,12 +116,28 @@ class _LatihanPageState extends State<LatihanPage> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: Padding(
|
body: FutureBuilder<List<dynamic>>(
|
||||||
|
future: latihanData,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
|
return Center(child: CircularProgressIndicator()); // Tampilkan loading selama data dimuat
|
||||||
|
} else if (snapshot.hasError) {
|
||||||
|
return Center(child: Text('Error: ${snapshot.error}')); // Menampilkan pesan error
|
||||||
|
} else if (!snapshot.hasData || snapshot.data == null || snapshot.data!.isEmpty) {
|
||||||
|
return Center(child: Text('Tidak ada latihan tersedia')); // Menampilkan pesan jika tidak ada data latihan
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ambil data latihan berdasarkan currentStep
|
||||||
|
final latihanList = snapshot.data!;
|
||||||
|
final latihan = latihanList[widget.currentStep]; // Ambil data latihan untuk currentStep
|
||||||
|
|
||||||
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 10.0),
|
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 10.0),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
|
SizedBox(height: 10),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -108,6 +182,7 @@ class _LatihanPageState extends State<LatihanPage> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 35.0, vertical: 40.0),
|
padding: const EdgeInsets.symmetric(horizontal: 35.0, vertical: 40.0),
|
||||||
margin: const EdgeInsets.symmetric(vertical: 10),
|
margin: const EdgeInsets.symmetric(vertical: 10),
|
||||||
|
|
@ -136,7 +211,7 @@ class _LatihanPageState extends State<LatihanPage> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
arabicText,
|
latihan['potongan_ayat'], // Menampilkan potongan ayat dari data latihan
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 30,
|
fontSize: 30,
|
||||||
color: Colors.red,
|
color: Colors.red,
|
||||||
|
|
@ -146,7 +221,7 @@ class _LatihanPageState extends State<LatihanPage> {
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Text(
|
Text(
|
||||||
latinText,
|
latihan['latin_text'], // Menampilkan teks latin dari data latihan
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
|
|
@ -156,8 +231,8 @@ class _LatihanPageState extends State<LatihanPage> {
|
||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// Play the audio file when the icon is clicked
|
// Memutar audio dari asset sesuai dengan URL yang ada pada `correct_audio`
|
||||||
FlameAudio.play(audioFile); // Play the audio using flame_audio
|
playAudio('audio/${latihan['correct_audio']}');
|
||||||
},
|
},
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.volume_up,
|
Icons.volume_up,
|
||||||
|
|
@ -176,26 +251,28 @@ class _LatihanPageState extends State<LatihanPage> {
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
if (!isRecording) {
|
||||||
if (currentStep < latihanList.length - 1) {
|
startTimer(); // Start timer and change to stop icon
|
||||||
currentStep++;
|
} else {
|
||||||
}
|
stopTimer(); // Stop timer and reset the button
|
||||||
});
|
// Pass data to PelafalanPage after recording
|
||||||
|
|
||||||
context.go(
|
context.go(
|
||||||
'/pelafalan',
|
'/pelafalan',
|
||||||
extra: {
|
extra: {
|
||||||
'currentStep': currentStep,
|
'id': widget.id,
|
||||||
|
'currentStep': widget.currentStep,
|
||||||
|
'latihanData': snapshot.data, // Mengirimkan data latihan yang sudah di-fetch
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: secondPrimaryColor,
|
backgroundColor: secondPrimaryColor,
|
||||||
shape: const CircleBorder(),
|
shape: const CircleBorder(),
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
),
|
),
|
||||||
child: const Icon(
|
child: Icon(
|
||||||
Icons.mic,
|
isRecording ? Icons.stop : Icons.mic, // Toggle between mic and stop icon
|
||||||
size: 40,
|
size: 40,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
|
|
@ -206,7 +283,15 @@ class _LatihanPageState extends State<LatihanPage> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_audioPlayer.dispose(); // Jangan lupa untuk melepaskan player
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,25 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:ta_tahsin/core/theme.dart';
|
import 'package:ta_tahsin/core/theme.dart';
|
||||||
import 'model/model_data_pelafalan.dart';
|
|
||||||
|
|
||||||
class PelafalanPage extends StatelessWidget {
|
class PelafalanPage extends StatelessWidget {
|
||||||
|
final int id;
|
||||||
final int currentStep;
|
final int currentStep;
|
||||||
|
final List<dynamic> latihanData; // Menerima semua data latihan
|
||||||
|
|
||||||
const PelafalanPage({
|
const PelafalanPage({
|
||||||
super.key,
|
super.key,
|
||||||
|
required this.id,
|
||||||
required this.currentStep,
|
required this.currentStep,
|
||||||
|
required this.latihanData,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// Ensure that currentStep is within the bounds of pelafalanList
|
// Ambil data latihan berdasarkan currentStep
|
||||||
if (currentStep >= pelafalanList.length) {
|
final latihan = latihanData[currentStep];
|
||||||
return Center(child: Text("End of List"));
|
|
||||||
}
|
|
||||||
|
|
||||||
final arabicText = pelafalanList[currentStep]['arabicText'];
|
|
||||||
final latinText = pelafalanList[currentStep]['latinText'];
|
|
||||||
final materiDescription = pelafalanList[currentStep]['materiDescription'];
|
|
||||||
final correctAudio = pelafalanList[currentStep]['correctAudio'];
|
|
||||||
final recordedAudio = pelafalanList[currentStep]['recordedAudio'];
|
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: Center(
|
body: Center(
|
||||||
|
|
@ -59,7 +56,7 @@ class PelafalanPage extends StatelessWidget {
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Center(
|
Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
arabicText,
|
latihan['potongan_ayat'],
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 30,
|
fontSize: 30,
|
||||||
color: Colors.red,
|
color: Colors.red,
|
||||||
|
|
@ -154,7 +151,7 @@ class PelafalanPage extends StatelessWidget {
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Text(
|
Text(
|
||||||
materiDescription,
|
latihan['materi_description'],
|
||||||
style: TextStyle(fontSize: 14, color: Colors.black),
|
style: TextStyle(fontSize: 14, color: Colors.black),
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
),
|
),
|
||||||
|
|
@ -164,11 +161,17 @@ class PelafalanPage extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// Update the progress and navigate back to LatihanPage
|
if (currentStep < latihanData.length - 1) {
|
||||||
context.go(
|
context.go(
|
||||||
'/latihan',
|
'/latihan',
|
||||||
extra: {'currentStep': currentStep + 1}, // Move to next step
|
extra: {
|
||||||
|
'id': id, // ganti sesuai kebutuhan
|
||||||
|
'currentStep': currentStep + 1,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
context.go('/navigasi');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: secondPrimaryColor,
|
backgroundColor: secondPrimaryColor,
|
||||||
|
|
@ -179,7 +182,7 @@ class PelafalanPage extends StatelessWidget {
|
||||||
minimumSize: Size(double.infinity, 50),
|
minimumSize: Size(double.infinity, 50),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Lanjut',
|
currentStep == latihanData.length - 1 ? 'Selesai' : 'Lanjut',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: whiteColor,
|
color: whiteColor,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
|
|
@ -190,7 +193,14 @@ class PelafalanPage extends StatelessWidget {
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.pop();
|
context.go(
|
||||||
|
'/latihan',
|
||||||
|
extra: {
|
||||||
|
'id': 1, // Ganti sesuai kebutuhan
|
||||||
|
'currentStep': currentStep,
|
||||||
|
'latihanData': latihanData, // Mengirimkan data latihan yang sesuai
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
|
|
@ -219,4 +229,3 @@ class PelafalanPage extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,27 +59,34 @@ class _MateriPageState extends State<MateriPage> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: PreferredSize(
|
||||||
backgroundColor: secondPrimaryColor,
|
preferredSize: Size.fromHeight(50), // Ukuran tinggi AppBar
|
||||||
|
child: Card(
|
||||||
|
elevation: 4, // Menambahkan shadow
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.zero, // Tidak ada radius, sudut tajam
|
||||||
|
),
|
||||||
|
margin: EdgeInsets.zero, // Menghilangkan margin Card
|
||||||
|
child: AppBar(
|
||||||
|
backgroundColor: secondPrimaryColor, // Warna AppBar (sesuaikan dengan secondPrimaryColor)
|
||||||
title: Text(
|
title: Text(
|
||||||
widget.title,
|
widget.title,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: whiteColor,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: const Icon(Icons.arrow_back), color: whiteColor,
|
icon: const Icon(Icons.arrow_back),
|
||||||
|
color: Colors.white,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (context.canPop()) {
|
|
||||||
context.pop();
|
|
||||||
} else {
|
|
||||||
context.go('/navigasi');
|
context.go('/navigasi');
|
||||||
}
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
body: isLoading
|
body: isLoading
|
||||||
? Center(child: CircularProgressIndicator()) // Tampilkan loading spinner selama proses loading
|
? Center(child: CircularProgressIndicator()) // Tampilkan loading spinner selama proses loading
|
||||||
: FutureBuilder<List<dynamic>>(
|
: FutureBuilder<List<dynamic>>(
|
||||||
|
|
@ -193,6 +200,7 @@ class _MateriPageState extends State<MateriPage> {
|
||||||
context.push(
|
context.push(
|
||||||
'/submateri', // Gantilah dengan rute yang sesuai
|
'/submateri', // Gantilah dengan rute yang sesuai
|
||||||
extra: {
|
extra: {
|
||||||
|
'id': submateri['id'],
|
||||||
'title': submateri['title'],
|
'title': submateri['title'],
|
||||||
'description': submateri['subtitle'],
|
'description': submateri['subtitle'],
|
||||||
'videoLink': submateri['video_url'],
|
'videoLink': submateri['video_url'],
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import 'package:ta_tahsin/core/theme.dart';
|
||||||
import 'package:youtube_player_flutter/youtube_player_flutter.dart'; // Import youtube_player_flutter
|
import 'package:youtube_player_flutter/youtube_player_flutter.dart'; // Import youtube_player_flutter
|
||||||
|
|
||||||
class SubMateriPage extends StatefulWidget {
|
class SubMateriPage extends StatefulWidget {
|
||||||
|
final int id;
|
||||||
final String title;
|
final String title;
|
||||||
final String description;
|
final String description;
|
||||||
final String videoLink;
|
final String videoLink;
|
||||||
|
|
@ -11,6 +12,7 @@ class SubMateriPage extends StatefulWidget {
|
||||||
|
|
||||||
const SubMateriPage({
|
const SubMateriPage({
|
||||||
super.key,
|
super.key,
|
||||||
|
required this.id,
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.description,
|
required this.description,
|
||||||
required this.videoLink,
|
required this.videoLink,
|
||||||
|
|
@ -29,36 +31,42 @@ class _SubMateriPageState extends State<SubMateriPage> {
|
||||||
super.initState();
|
super.initState();
|
||||||
_controller = YoutubePlayerController(
|
_controller = YoutubePlayerController(
|
||||||
initialVideoId: YoutubePlayer.convertUrlToId(widget.videoLink)!,
|
initialVideoId: YoutubePlayer.convertUrlToId(widget.videoLink)!,
|
||||||
flags: const YoutubePlayerFlags(
|
flags: const YoutubePlayerFlags(autoPlay: false, mute: false),
|
||||||
autoPlay: false,
|
|
||||||
mute: false,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: PreferredSize(
|
||||||
|
preferredSize: Size.fromHeight(50), // Ukuran tinggi AppBar
|
||||||
|
child: Card(
|
||||||
|
elevation: 4, // Menambahkan shadow
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.zero, // Tidak ada radius, sudut tajam
|
||||||
|
),
|
||||||
|
margin: EdgeInsets.zero, // Menghilangkan margin Card
|
||||||
|
child: AppBar(
|
||||||
|
backgroundColor:
|
||||||
|
secondPrimaryColor, // Warna AppBar (sesuaikan dengan secondPrimaryColor)
|
||||||
title: Text(
|
title: Text(
|
||||||
widget.title,
|
widget.title,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 25,
|
fontSize: 24,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: blackColor,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: const Icon(Icons.arrow_back),
|
icon: const Icon(Icons.arrow_back),
|
||||||
|
color: Colors.white,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (context.canPop()) {
|
Navigator.pop(context); // Aksi kembali
|
||||||
context.pop();
|
|
||||||
} else {
|
|
||||||
context.go('/');
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
body: Column(
|
body: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -75,7 +83,7 @@ class _SubMateriPageState extends State<SubMateriPage> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
color: secondPrimaryColor,
|
color: secondPrimaryColor1,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 10),
|
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 10),
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: Text(
|
child: Text(
|
||||||
|
|
@ -83,7 +91,7 @@ class _SubMateriPageState extends State<SubMateriPage> {
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: whiteColor,
|
color: blackColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -92,10 +100,7 @@ class _SubMateriPageState extends State<SubMateriPage> {
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
widget.intro,
|
widget.intro,
|
||||||
style: TextStyle(
|
style: TextStyle(fontSize: 16, color: blackColor),
|
||||||
fontSize: 16,
|
|
||||||
color: blackColor,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|
@ -103,21 +108,39 @@ class _SubMateriPageState extends State<SubMateriPage> {
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 20),
|
padding: const EdgeInsets.symmetric(
|
||||||
child: ElevatedButton(
|
horizontal: 16.0,
|
||||||
style: ElevatedButton.styleFrom(
|
vertical: 20,
|
||||||
backgroundColor: secondPrimaryColor,
|
),
|
||||||
shape: RoundedRectangleBorder(
|
child: Container(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 50,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: secondPrimaryColor,
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.1),
|
||||||
|
offset: Offset(0, -4),
|
||||||
|
blurRadius: 6,
|
||||||
),
|
),
|
||||||
minimumSize: Size(double.infinity, 50),
|
],
|
||||||
),
|
),
|
||||||
|
child: TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.go('/latihan');
|
context.go(
|
||||||
|
'/latihan', // Gantilah dengan rute yang sesuai
|
||||||
|
extra: {'id': widget.id}, // Mengirimkan id dari halaman sebelumnya
|
||||||
|
);
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
"Mulai Berlatih",
|
"Mulai Berlatih",
|
||||||
style: TextStyle(fontSize: 16,color: whiteColor),
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue