|
@@ -1,4 +1,6 @@
|
|
|
import 'package:flutter/material.dart';
|
|
|
+import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
|
|
|
+import 'package:pm25go/scan.dart';
|
|
|
|
|
|
void main() {
|
|
|
runApp(const MyApp());
|
|
@@ -22,13 +24,33 @@ class MyApp extends StatelessWidget {
|
|
|
// 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.deepPurple,
|
|
|
+ primarySwatch: createMaterialColor(Color.fromARGB(255, 64, 155, 253)),
|
|
|
),
|
|
|
home: const MyHomePage(title: 'Pocket PM2.5 Monitor'),
|
|
|
);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+MaterialColor createMaterialColor(Color color) {
|
|
|
+ List strengths = <double>[.05];
|
|
|
+ final swatch = <int, Color>{};
|
|
|
+ final int r = color.red, g = color.green, b = color.blue;
|
|
|
+
|
|
|
+ for (int i = 1; i < 10; i++) {
|
|
|
+ strengths.add(0.1 * i);
|
|
|
+ }
|
|
|
+ strengths.forEach((strength) {
|
|
|
+ final double ds = 0.5 - strength;
|
|
|
+ swatch[(strength * 1000).round()] = Color.fromRGBO(
|
|
|
+ r + ((ds < 0 ? r : (255 - r)) * ds).round(),
|
|
|
+ g + ((ds < 0 ? g : (255 - g)) * ds).round(),
|
|
|
+ b + ((ds < 0 ? b : (255 - b)) * ds).round(),
|
|
|
+ 1,
|
|
|
+ );
|
|
|
+ });
|
|
|
+ return MaterialColor(color.value, swatch);
|
|
|
+}
|
|
|
+
|
|
|
class MyHomePage extends StatefulWidget {
|
|
|
const MyHomePage({super.key, required this.title});
|
|
|
|
|
@@ -48,23 +70,50 @@ class MyHomePage extends StatefulWidget {
|
|
|
}
|
|
|
|
|
|
class _MyHomePageState extends State<MyHomePage> {
|
|
|
- int _counter = 0;
|
|
|
- var _statusText = "Waiting for Bluetooth Connection...";
|
|
|
+ var _statusText = "正在等待藍牙運接…\nWaiting for Bluetooth Connection...";
|
|
|
+
|
|
|
+ //Bluetooth Connection
|
|
|
+ bool _btConnected = false;
|
|
|
+ var _btAddr = "";
|
|
|
|
|
|
+ //Data update for display
|
|
|
int _pm01 = 0; //No recommended data found
|
|
|
int _pm25 = 0; //Healthy range: 35, recommend < 15
|
|
|
int _pm10 = 0; //Healthy range: 150, recommend < 50
|
|
|
- void _incrementCounter() {
|
|
|
+ void _toggleBluetooth() {
|
|
|
setState(() {
|
|
|
// This call to setState tells the Flutter framework that something has
|
|
|
// changed in this State, which causes it to rerun the build method below
|
|
|
// so that the display can reflect the updated values. If we changed
|
|
|
// _counter without calling setState(), then the build method would not be
|
|
|
// called again, and so nothing would appear to happen.
|
|
|
- _counter++;
|
|
|
+ Navigator.push(
|
|
|
+ context,
|
|
|
+ MaterialPageRoute(builder: (context) => const ConnectionPage()),
|
|
|
+ );
|
|
|
+ return;
|
|
|
+ if (_btConnected) {
|
|
|
+ //Disconnect the current bt connection
|
|
|
+ _statusText = "已斷開連接\nDisconnected";
|
|
|
+ _btConnected = false;
|
|
|
+ } else {
|
|
|
+ //Connect to the target bluetooth module
|
|
|
+ _statusText = "正在連接口袋 PM2.5 傳感器…\nConnecting to Pocket PM2.5 Sensor...";
|
|
|
+
|
|
|
+ _connectBluetoothDevice();
|
|
|
+ }
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+ void _connectBluetoothDevice() {
|
|
|
+ try {
|
|
|
+ _btConnected = true;
|
|
|
+ } catch (exception) {
|
|
|
+ //Connection to BT failed
|
|
|
+ _btConnected = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
@override
|
|
|
Widget build(BuildContext context) {
|
|
|
// This method is rerun every time setState is called, for instance as done
|
|
@@ -75,11 +124,13 @@ class _MyHomePageState extends State<MyHomePage> {
|
|
|
// than having to individually change instances of widgets.
|
|
|
return Scaffold(
|
|
|
resizeToAvoidBottomInset: false,
|
|
|
- appBar: AppBar(
|
|
|
+ appBar:
|
|
|
+ null /*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: SingleChildScrollView(
|
|
|
child: Center(
|
|
|
// Center is a layout widget. It takes a single child and positions it
|
|
@@ -101,6 +152,8 @@ class _MyHomePageState extends State<MyHomePage> {
|
|
|
// horizontal).
|
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
|
children: <Widget>[
|
|
|
+ //Title banner
|
|
|
+ Image.asset('assets/images/banner.png'),
|
|
|
//Status Text
|
|
|
Container(
|
|
|
width: double.infinity,
|
|
@@ -122,8 +175,15 @@ class _MyHomePageState extends State<MyHomePage> {
|
|
|
height: 120,
|
|
|
color: Colors.blue,
|
|
|
child: Text(
|
|
|
- "",
|
|
|
- style: TextStyle(color: Colors.white),
|
|
|
+ (() {
|
|
|
+ if (_pm01 == 0) {
|
|
|
+ return "無數據";
|
|
|
+ } else if (_pm01 < 35) {
|
|
|
+ return "良好";
|
|
|
+ }
|
|
|
+ return "一般";
|
|
|
+ }()),
|
|
|
+ style: const TextStyle(color: Colors.white),
|
|
|
)),
|
|
|
),
|
|
|
Expanded(
|
|
@@ -131,17 +191,17 @@ class _MyHomePageState extends State<MyHomePage> {
|
|
|
child: Container(
|
|
|
color: Colors.white,
|
|
|
height: 120,
|
|
|
- padding: EdgeInsetsDirectional.only(top: 35),
|
|
|
+ padding: const EdgeInsetsDirectional.only(top: 30),
|
|
|
child: Column(
|
|
|
children: [
|
|
|
- Text(
|
|
|
- "PM 1.0 Reading",
|
|
|
+ const Text(
|
|
|
+ "PM 1.0 數值",
|
|
|
textAlign: TextAlign.center,
|
|
|
),
|
|
|
Text(
|
|
|
'$_pm01 μg/m^3',
|
|
|
textAlign: TextAlign.center,
|
|
|
- style: TextStyle(fontSize: 30),
|
|
|
+ style: const TextStyle(fontSize: 30),
|
|
|
)
|
|
|
],
|
|
|
)),
|
|
@@ -160,7 +220,17 @@ class _MyHomePageState extends State<MyHomePage> {
|
|
|
height: 120,
|
|
|
color: Colors.grey,
|
|
|
child: Text(
|
|
|
- "NO DATA",
|
|
|
+ (() {
|
|
|
+ if (_pm25 == 0) {
|
|
|
+ return "無數據";
|
|
|
+ } else if (_pm25 < 15) {
|
|
|
+ return "良好";
|
|
|
+ } else if (_pm25 < 35) {
|
|
|
+ return "一般";
|
|
|
+ }
|
|
|
+
|
|
|
+ return "差";
|
|
|
+ }()),
|
|
|
style: TextStyle(color: Colors.white),
|
|
|
)),
|
|
|
),
|
|
@@ -169,11 +239,11 @@ class _MyHomePageState extends State<MyHomePage> {
|
|
|
child: Container(
|
|
|
color: Colors.white,
|
|
|
height: 120,
|
|
|
- padding: EdgeInsetsDirectional.only(top: 35),
|
|
|
+ padding: EdgeInsetsDirectional.only(top: 30),
|
|
|
child: Column(
|
|
|
children: [
|
|
|
Text(
|
|
|
- "PM 2.5 Reading",
|
|
|
+ "PM 2.5 數值",
|
|
|
textAlign: TextAlign.center,
|
|
|
),
|
|
|
Text(
|
|
@@ -198,7 +268,17 @@ class _MyHomePageState extends State<MyHomePage> {
|
|
|
height: 120,
|
|
|
color: Colors.grey,
|
|
|
child: Text(
|
|
|
- "NO DATA",
|
|
|
+ (() {
|
|
|
+ if (_pm10 == 0) {
|
|
|
+ return "無數據";
|
|
|
+ } else if (_pm10 < 50) {
|
|
|
+ return "良好";
|
|
|
+ } else if (_pm10 < 150) {
|
|
|
+ return "一般";
|
|
|
+ }
|
|
|
+
|
|
|
+ return "差";
|
|
|
+ }()),
|
|
|
style: TextStyle(color: Colors.white),
|
|
|
)),
|
|
|
),
|
|
@@ -207,11 +287,11 @@ class _MyHomePageState extends State<MyHomePage> {
|
|
|
child: Container(
|
|
|
color: Colors.white,
|
|
|
height: 120,
|
|
|
- padding: EdgeInsetsDirectional.only(top: 35),
|
|
|
+ padding: EdgeInsetsDirectional.only(top: 30),
|
|
|
child: Column(
|
|
|
children: [
|
|
|
Text(
|
|
|
- "PM 10.0 Reading",
|
|
|
+ "PM 10.0 數值",
|
|
|
textAlign: TextAlign.center,
|
|
|
),
|
|
|
Text(
|
|
@@ -227,8 +307,8 @@ class _MyHomePageState extends State<MyHomePage> {
|
|
|
),
|
|
|
)),
|
|
|
floatingActionButton: FloatingActionButton(
|
|
|
- onPressed: _incrementCounter,
|
|
|
- tooltip: 'Connect To Bluetooth',
|
|
|
+ onPressed: _toggleBluetooth,
|
|
|
+ tooltip: '連接藍牙裝置',
|
|
|
child: const Icon(Icons.bluetooth),
|
|
|
), // This trailing comma makes auto-formatting nicer for build methods.
|
|
|
);
|