Compare commits
No commits in common. "03eaa16842f02fca4245318f6d81c81658580aa2" and "ebf389357d007e6d1e9b7a1ac37424b4cc2a8b3d" have entirely different histories.
03eaa16842
...
ebf389357d
|
@ -1 +0,0 @@
|
||||||
{}
|
|
|
@ -2,7 +2,6 @@
|
||||||
package="com.example.fmassive">
|
package="com.example.fmassive">
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
|
|
191
android/app/src/main/kotlin/com/example/fmassive/Alarm.kt
Normal file
191
android/app/src/main/kotlin/com/example/fmassive/Alarm.kt
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
package com.example.fmassive
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.*
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.CountDownTimer
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import io.flutter.plugin.common.MethodCall
|
||||||
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
||||||
|
import kotlin.math.floor
|
||||||
|
|
||||||
|
|
||||||
|
class AlarmModule(private val context: Context) : MethodCallHandler {
|
||||||
|
|
||||||
|
private var countdownTimer: CountDownTimer? = null
|
||||||
|
var currentMs: Long = 0
|
||||||
|
private var running = false
|
||||||
|
|
||||||
|
private val stopReceiver = object : BroadcastReceiver() {
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
|
Log.d("AlarmModule", "Received stop broadcast intent")
|
||||||
|
if (context != null) {
|
||||||
|
stop(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val addReceiver = object : BroadcastReceiver() {
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
|
if (context != null) {
|
||||||
|
add(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
|
fun add(context: Context) {
|
||||||
|
Log.d("AlarmModule", "Add 1 min to alarm.")
|
||||||
|
countdownTimer?.cancel()
|
||||||
|
val newMs = if (running) currentMs.toInt().plus(60000) else 60000
|
||||||
|
countdownTimer = getTimer(context, newMs)
|
||||||
|
countdownTimer?.start()
|
||||||
|
running = true
|
||||||
|
//val manager = getManager()
|
||||||
|
//manager.cancel(AlarmService.NOTIFICATION_ID_DONE)
|
||||||
|
//val intent = Intent(context, AlarmService::class.java)
|
||||||
|
//context.stopService(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
|
fun stop(context: Context) {
|
||||||
|
Log.d("AlarmModule", "Stop alarm.")
|
||||||
|
countdownTimer?.cancel()
|
||||||
|
running = false
|
||||||
|
//val intent = Intent(context, AlarmService::class.java)
|
||||||
|
//reactApplicationContext?.stopService(intent)
|
||||||
|
//val manager = getManager()
|
||||||
|
//manager.cancel(AlarmService.NOTIFICATION_ID_DONE)
|
||||||
|
//manager.cancel(NOTIFICATION_ID_PENDING)
|
||||||
|
//val params = Arguments.createMap().apply {
|
||||||
|
// putString("minutes", "00")
|
||||||
|
// putString("seconds", "00")
|
||||||
|
//}
|
||||||
|
//reactApplicationContext
|
||||||
|
// .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
||||||
|
// .emit("tick", params)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
|
fun timer(context: Context, milliseconds: Int) {
|
||||||
|
context.registerReceiver(stopReceiver, IntentFilter(STOP_BROADCAST))
|
||||||
|
context.registerReceiver(addReceiver, IntentFilter(ADD_BROADCAST))
|
||||||
|
Log.d("AlarmModule", "Queue alarm for $milliseconds delay")
|
||||||
|
val manager = getManager(context)
|
||||||
|
//manager.cancel(AlarmService.NOTIFICATION_ID_DONE)
|
||||||
|
//val intent = Intent(reactApplicationContext, AlarmService::class.java)
|
||||||
|
//reactApplicationContext.stopService(intent)
|
||||||
|
countdownTimer?.cancel()
|
||||||
|
countdownTimer = getTimer(context, milliseconds)
|
||||||
|
countdownTimer?.start()
|
||||||
|
running = true
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.M)
|
||||||
|
private fun getTimer(
|
||||||
|
context: Context,
|
||||||
|
endMs: Int,
|
||||||
|
): CountDownTimer {
|
||||||
|
val builder = getBuilder(context)
|
||||||
|
return object : CountDownTimer(endMs.toLong(), 1000) {
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
override fun onTick(current: Long) {
|
||||||
|
currentMs = current
|
||||||
|
val seconds =
|
||||||
|
floor((current / 1000).toDouble() % 60).toInt().toString().padStart(2, '0')
|
||||||
|
val minutes =
|
||||||
|
floor((current / 1000).toDouble() / 60).toInt().toString().padStart(2, '0')
|
||||||
|
builder.setContentText("$minutes:$seconds").setAutoCancel(false).setDefaults(0)
|
||||||
|
.setProgress(endMs, current.toInt(), false)
|
||||||
|
.setCategory(NotificationCompat.CATEGORY_PROGRESS).priority =
|
||||||
|
NotificationCompat.PRIORITY_LOW
|
||||||
|
val manager = getManager(context)
|
||||||
|
manager.notify(NOTIFICATION_ID_PENDING, builder.build())
|
||||||
|
//val params = Arguments.createMap().apply {
|
||||||
|
// putString("minutes", minutes)
|
||||||
|
// putString("seconds", seconds)
|
||||||
|
//}
|
||||||
|
//reactApplicationContext
|
||||||
|
// .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
||||||
|
// .emit("tick", params)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
override fun onFinish() {
|
||||||
|
//val context = reactApplicationContext
|
||||||
|
//context.startForegroundService(Intent(context, AlarmService::class.java))
|
||||||
|
//context
|
||||||
|
// .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
||||||
|
// .emit("finish", Arguments.createMap().apply {
|
||||||
|
// putString("minutes", "00")
|
||||||
|
// putString("seconds", "00")
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("UnspecifiedImmutableFlag")
|
||||||
|
@RequiresApi(Build.VERSION_CODES.M)
|
||||||
|
private fun getBuilder(context: Context): NotificationCompat.Builder {
|
||||||
|
val contentIntent = Intent(context, MainActivity::class.java)
|
||||||
|
val pendingContent =
|
||||||
|
PendingIntent.getActivity(context, 0, contentIntent, PendingIntent.FLAG_IMMUTABLE)
|
||||||
|
val addBroadcast = Intent(ADD_BROADCAST).apply {
|
||||||
|
setPackage(context.packageName)
|
||||||
|
}
|
||||||
|
val pendingAdd =
|
||||||
|
PendingIntent.getBroadcast(context, 0, addBroadcast, PendingIntent.FLAG_MUTABLE)
|
||||||
|
val stopBroadcast = Intent(STOP_BROADCAST)
|
||||||
|
stopBroadcast.setPackage(context.packageName)
|
||||||
|
val pendingStop =
|
||||||
|
PendingIntent.getBroadcast(context, 0, stopBroadcast, PendingIntent.FLAG_IMMUTABLE)
|
||||||
|
return NotificationCompat.Builder(context, CHANNEL_ID_PENDING)
|
||||||
|
.setSmallIcon(R.drawable.ic_baseline_hourglass_bottom_24).setContentTitle("Resting")
|
||||||
|
.setContentIntent(pendingContent)
|
||||||
|
.addAction(R.drawable.ic_baseline_stop_24, "Stop", pendingStop)
|
||||||
|
.addAction(R.drawable.ic_baseline_stop_24, "Add 1 min", pendingAdd)
|
||||||
|
.setDeleteIntent(pendingStop)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
private fun getManager(context: Context): NotificationManager {
|
||||||
|
val notificationManager = context.getSystemService(
|
||||||
|
NotificationManager::class.java
|
||||||
|
)
|
||||||
|
val timersChannel = NotificationChannel(
|
||||||
|
CHANNEL_ID_PENDING, CHANNEL_ID_PENDING, NotificationManager.IMPORTANCE_LOW
|
||||||
|
)
|
||||||
|
timersChannel.setSound(null, null)
|
||||||
|
timersChannel.description = "Progress on rest timers."
|
||||||
|
notificationManager.createNotificationChannel(timersChannel)
|
||||||
|
return notificationManager
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val STOP_BROADCAST = "stop-timer-event"
|
||||||
|
const val ADD_BROADCAST = "add-timer-event"
|
||||||
|
const val CHANNEL_ID_PENDING = "Timer"
|
||||||
|
const val NOTIFICATION_ID_PENDING = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||||
|
when (call.method) {
|
||||||
|
"timer" -> {
|
||||||
|
|
||||||
|
}
|
||||||
|
else -> result.notImplemented()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,10 +25,11 @@ class MainActivity : FlutterActivity() {
|
||||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||||
super.configureFlutterEngine(flutterEngine)
|
super.configureFlutterEngine(flutterEngine)
|
||||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
|
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
|
||||||
|
// This method is invoked on the main thread.
|
||||||
call, result ->
|
call, result ->
|
||||||
if (call.method == "timer") {
|
if (call.method == "timer") {
|
||||||
val args = call.arguments as ArrayList<*>
|
val args = call.arguments as ArrayList<Long>
|
||||||
timer(args[0] as Long)
|
timer(args[0])
|
||||||
} else {
|
} else {
|
||||||
result.notImplemented()
|
result.notImplemented()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.example.fmassive
|
||||||
|
|
||||||
|
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||||
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
|
||||||
|
class MyFlutterPlugin : FlutterPlugin {
|
||||||
|
private lateinit var channel: MethodChannel
|
||||||
|
|
||||||
|
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
|
||||||
|
val context = binding.applicationContext
|
||||||
|
val handler = AlarmModule(context)
|
||||||
|
channel = MethodChannel(binding.binaryMessenger, "my_channel")
|
||||||
|
channel.setMethodCallHandler(handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
|
||||||
|
channel.setMethodCallHandler(null)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,17 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class BestPage extends StatelessWidget {
|
|
||||||
const BestPage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: const Text('Best'),
|
|
||||||
),
|
|
||||||
body: const Center(
|
|
||||||
child: Text('Welcome to the Best Page!'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:fmassive/home_page.dart';
|
|
||||||
|
|
||||||
class EditGymSetPage extends StatefulWidget {
|
|
||||||
final GymSet gymSet;
|
|
||||||
|
|
||||||
EditGymSetPage({required this.gymSet});
|
|
||||||
|
|
||||||
@override
|
|
||||||
_EditGymSetPageState createState() => _EditGymSetPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _EditGymSetPageState extends State<EditGymSetPage> {
|
|
||||||
final TextEditingController _nameController = TextEditingController();
|
|
||||||
final TextEditingController _repsController = TextEditingController();
|
|
||||||
final TextEditingController _weightController = TextEditingController();
|
|
||||||
|
|
||||||
late GymSet _editedGymSet;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
// Initialize the edited GymSet object with the values from the input GymSet object
|
|
||||||
_editedGymSet = GymSet(
|
|
||||||
name: widget.gymSet.name,
|
|
||||||
reps: widget.gymSet.reps,
|
|
||||||
weight: widget.gymSet.weight,
|
|
||||||
created: DateTime.now());
|
|
||||||
// Set the text controller values
|
|
||||||
_nameController.text = _editedGymSet.name;
|
|
||||||
_repsController.text = _editedGymSet.reps.toString();
|
|
||||||
_weightController.text = _editedGymSet.weight.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: Text('Edit Gym Set'),
|
|
||||||
),
|
|
||||||
body: Padding(
|
|
||||||
padding: const EdgeInsets.all(16.0),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
TextFormField(
|
|
||||||
controller: _nameController,
|
|
||||||
decoration: const InputDecoration(labelText: 'Name'),
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() {
|
|
||||||
_editedGymSet.name = value;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
TextFormField(
|
|
||||||
controller: _repsController,
|
|
||||||
decoration: const InputDecoration(labelText: 'Reps'),
|
|
||||||
keyboardType: TextInputType.number,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() {
|
|
||||||
_editedGymSet.reps = int.tryParse(value) ?? 0;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
TextFormField(
|
|
||||||
controller: _weightController,
|
|
||||||
decoration: const InputDecoration(labelText: 'Weight (kg)'),
|
|
||||||
keyboardType: TextInputType.number,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() {
|
|
||||||
_editedGymSet.weight = int.tryParse(value) ?? 0;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
floatingActionButton: FloatingActionButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context, _editedGymSet);
|
|
||||||
},
|
|
||||||
child: const Icon(Icons.check),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,172 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:fmassive/edit_set.dart';
|
|
||||||
|
|
||||||
class HomePage extends StatelessWidget {
|
|
||||||
HomePage({super.key});
|
|
||||||
|
|
||||||
final List<Map<String, dynamic>> _pageData = [
|
|
||||||
{'title': 'Home', 'icon': Icons.home},
|
|
||||||
{'title': 'Plans', 'icon': Icons.calendar_today},
|
|
||||||
{'title': 'Best', 'icon': Icons.star},
|
|
||||||
{'title': 'Workouts', 'icon': Icons.fitness_center},
|
|
||||||
{'title': 'Timer', 'icon': Icons.timer},
|
|
||||||
{'title': 'Settings', 'icon': Icons.settings},
|
|
||||||
];
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: Text(_pageData[0]['title']),
|
|
||||||
),
|
|
||||||
drawer: Drawer(
|
|
||||||
child: ListView.builder(
|
|
||||||
itemCount: _pageData.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
return ListTile(
|
|
||||||
leading: Icon(_pageData[index]['icon']),
|
|
||||||
title: Text(_pageData[index]['title']),
|
|
||||||
onTap: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
Navigator.pushNamed(
|
|
||||||
context, '/${_pageData[index]['title'].toLowerCase()}');
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
body: const Center(
|
|
||||||
child: GymSetPage(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GymSet {
|
|
||||||
String name;
|
|
||||||
int reps;
|
|
||||||
int weight;
|
|
||||||
DateTime created;
|
|
||||||
|
|
||||||
GymSet(
|
|
||||||
{required this.name,
|
|
||||||
required this.reps,
|
|
||||||
required this.weight,
|
|
||||||
required this.created});
|
|
||||||
}
|
|
||||||
|
|
||||||
class GymSetPage extends StatefulWidget {
|
|
||||||
const GymSetPage({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_GymSetPageState createState() => _GymSetPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _GymSetPageState extends State<GymSetPage> {
|
|
||||||
final List<GymSet> _gymSets = [
|
|
||||||
GymSet(
|
|
||||||
name: "Bench press",
|
|
||||||
reps: 10,
|
|
||||||
weight: 50,
|
|
||||||
created: DateTime(2022, 1, 1)),
|
|
||||||
GymSet(
|
|
||||||
name: "Bench press",
|
|
||||||
reps: 8,
|
|
||||||
weight: 60,
|
|
||||||
created: DateTime(2022, 1, 2)),
|
|
||||||
GymSet(
|
|
||||||
name: "Bench press",
|
|
||||||
reps: 6,
|
|
||||||
weight: 70,
|
|
||||||
created: DateTime(2022, 1, 3)),
|
|
||||||
GymSet(
|
|
||||||
name: "Shoulder press",
|
|
||||||
reps: 12,
|
|
||||||
weight: 40,
|
|
||||||
created: DateTime(2022, 1, 4)),
|
|
||||||
GymSet(
|
|
||||||
name: "Shoulder press",
|
|
||||||
reps: 15,
|
|
||||||
weight: 35,
|
|
||||||
created: DateTime(2022, 1, 5)),
|
|
||||||
];
|
|
||||||
|
|
||||||
List<GymSet> _searchResults = [];
|
|
||||||
|
|
||||||
final TextEditingController _searchController = TextEditingController();
|
|
||||||
|
|
||||||
void _searchGymSets(String searchQuery) {
|
|
||||||
List<GymSet> results = [];
|
|
||||||
|
|
||||||
if (searchQuery.isEmpty) {
|
|
||||||
results = _gymSets;
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i < _gymSets.length; i++) {
|
|
||||||
if (_gymSets[i].reps.toString().contains(searchQuery) ||
|
|
||||||
_gymSets[i].weight.toString().contains(searchQuery) ||
|
|
||||||
_gymSets[i].created.toString().contains(searchQuery) ||
|
|
||||||
_gymSets[i].name.contains(searchQuery)) {
|
|
||||||
results.add(_gymSets[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
_searchResults = results;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
|
|
||||||
// Initialize the search results to all the gym sets
|
|
||||||
_searchResults = _gymSets;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
automaticallyImplyLeading: false,
|
|
||||||
title: TextField(
|
|
||||||
controller: _searchController,
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
hintText: 'Search Gym Sets',
|
|
||||||
border: InputBorder.none,
|
|
||||||
),
|
|
||||||
onChanged: (searchQuery) {
|
|
||||||
_searchGymSets(searchQuery);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
body: ListView.builder(
|
|
||||||
itemCount: _searchResults.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
return ListTile(
|
|
||||||
title: Text(
|
|
||||||
'${_searchResults[index].name}: ${_searchResults[index].reps}x${_searchResults[index].weight}kg'),
|
|
||||||
onTap: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) =>
|
|
||||||
EditGymSetPage(gymSet: _searchResults[index]),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
floatingActionButton: FloatingActionButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) =>
|
|
||||||
EditGymSetPage(gymSet: _searchResults[0]),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: const Icon(Icons.add)));
|
|
||||||
}
|
|
||||||
}
|
|
124
lib/main.dart
124
lib/main.dart
|
@ -1,45 +1,111 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:fmassive/best_page.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:fmassive/edit_set.dart';
|
|
||||||
import 'package:fmassive/home_page.dart';
|
|
||||||
import 'package:fmassive/plans_page.dart';
|
|
||||||
import 'package:fmassive/settings_page.dart';
|
|
||||||
import 'package:fmassive/timer_page.dart';
|
|
||||||
import 'package:fmassive/workouts_page.dart';
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
MyApp({Key? key}) : super(key: key);
|
const MyApp({super.key});
|
||||||
|
|
||||||
final navigatorKey = GlobalKey<NavigatorState>();
|
|
||||||
|
|
||||||
|
// This widget is the root of your application.
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final edit = EditGymSetPage(
|
|
||||||
gymSet: GymSet(name: '', reps: 0, weight: 0, created: DateTime.now()));
|
|
||||||
|
|
||||||
final Map<String, WidgetBuilder> routes = {
|
|
||||||
'/home': (context) => HomePage(),
|
|
||||||
'/plans': (context) => const PlansPage(),
|
|
||||||
'/best': (context) => const BestPage(),
|
|
||||||
'/workouts': (context) => const WorkoutsPage(),
|
|
||||||
'/timer': (context) => const TimerPage(),
|
|
||||||
'/settings': (context) => const SettingsPage(),
|
|
||||||
'/edit-set': (context) => edit,
|
|
||||||
};
|
|
||||||
|
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: 'Gym App',
|
title: 'Flutter Demo',
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
|
// This is the theme of your application.
|
||||||
|
//
|
||||||
|
// Try running your application with "flutter run". You'll see the
|
||||||
|
// application has a blue toolbar. Then, without quitting the app, try
|
||||||
|
// changing the primarySwatch below to Colors.green and then invoke
|
||||||
|
// "hot reload" (press "r" in the console where you ran "flutter run",
|
||||||
|
// or simply save your changes to "hot reload" in a Flutter IDE).
|
||||||
|
// Notice that the counter didn't reset back to zero; the application
|
||||||
|
// is not restarted.
|
||||||
primarySwatch: Colors.blue,
|
primarySwatch: Colors.blue,
|
||||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
|
||||||
),
|
),
|
||||||
initialRoute: '/home',
|
home: const MyHomePage(title: 'Flutter Demo Home Page'),
|
||||||
routes: routes,
|
);
|
||||||
navigatorKey: navigatorKey,
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyHomePage extends StatefulWidget {
|
||||||
|
const MyHomePage({super.key, required this.title});
|
||||||
|
|
||||||
|
// This widget is the home page of your application. It is stateful, meaning
|
||||||
|
// that it has a State object (defined below) that contains fields that affect
|
||||||
|
// how it looks.
|
||||||
|
|
||||||
|
// This class is the configuration for the state. It holds the values (in this
|
||||||
|
// case the title) provided by the parent (in this case the App widget) and
|
||||||
|
// used by the build method of the State. Fields in a Widget subclass are
|
||||||
|
// always marked "final".
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MyHomePage> createState() => _MyHomePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MyHomePageState extends State<MyHomePage> {
|
||||||
|
static const platform = MethodChannel('com.massive/android');
|
||||||
|
|
||||||
|
Future<void> _timer() async {
|
||||||
|
await platform.invokeMethod('timer', [3000]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
// This method is rerun every time setState is called, for instance as done
|
||||||
|
// by the _incrementCounter method above.
|
||||||
|
//
|
||||||
|
// The Flutter framework has been optimized to make rerunning build methods
|
||||||
|
// fast, so that you can just rebuild anything that needs updating rather
|
||||||
|
// than having to individually change instances of widgets.
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
// Here we take the value from the MyHomePage object that was created by
|
||||||
|
// the App.build method, and use it to set our appbar title.
|
||||||
|
title: Text(widget.title),
|
||||||
|
),
|
||||||
|
body: Center(
|
||||||
|
// Center is a layout widget. It takes a single child and positions it
|
||||||
|
// in the middle of the parent.
|
||||||
|
child: Column(
|
||||||
|
// Column is also a layout widget. It takes a list of children and
|
||||||
|
// arranges them vertically. By default, it sizes itself to fit its
|
||||||
|
// children horizontally, and tries to be as tall as its parent.
|
||||||
|
//
|
||||||
|
// Invoke "debug painting" (press "p" in the console, choose the
|
||||||
|
// "Toggle Debug Paint" action from the Flutter Inspector in Android
|
||||||
|
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
|
||||||
|
// to see the wireframe for each widget.
|
||||||
|
//
|
||||||
|
// Column has various properties to control how it sizes itself and
|
||||||
|
// how it positions its children. Here we use mainAxisAlignment to
|
||||||
|
// center the children vertically; the main axis here is the vertical
|
||||||
|
// axis because Columns are vertical (the cross axis would be
|
||||||
|
// horizontal).
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
const Text(
|
||||||
|
'You have pushed the button this many times:',
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Tap to start timer',
|
||||||
|
style: Theme.of(context).textTheme.headlineMedium,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
onPressed: _timer,
|
||||||
|
tooltip: 'Increment',
|
||||||
|
child: const Icon(Icons.add),
|
||||||
|
), // This trailing comma makes auto-formatting nicer for build methods.
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class PlansPage extends StatelessWidget {
|
|
||||||
const PlansPage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: const Text('Plans'),
|
|
||||||
),
|
|
||||||
body: const Center(
|
|
||||||
child: Text('Welcome to the Plans Page!'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class SettingsPage extends StatelessWidget {
|
|
||||||
const SettingsPage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: const Text('Settings'),
|
|
||||||
),
|
|
||||||
body: const Center(
|
|
||||||
child: Text('Welcome to the Settings Page!'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class TimerPage extends StatelessWidget {
|
|
||||||
const TimerPage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: const Text('Timer'),
|
|
||||||
),
|
|
||||||
body: const Center(
|
|
||||||
child: Text('Welcome to the Timer Page!'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class WorkoutsPage extends StatelessWidget {
|
|
||||||
const WorkoutsPage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: const Text('Workouts'),
|
|
||||||
),
|
|
||||||
body: const Center(
|
|
||||||
child: Text('Welcome to the Workouts Page!'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,12 +7,13 @@
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
import 'package:fmassive/main.dart';
|
import 'package:fmassive/main.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||||
// Build our app and trigger a frame.
|
// Build our app and trigger a frame.
|
||||||
await tester.pumpWidget(MyApp());
|
await tester.pumpWidget(const MyApp());
|
||||||
|
|
||||||
// Verify that our counter starts at 0.
|
// Verify that our counter starts at 0.
|
||||||
expect(find.text('0'), findsOneWidget);
|
expect(find.text('0'), findsOneWidget);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user