Stop listening to a stream - flutter

This program works okay on first build.When I disconnect my device and reconnects it,it is showing, bad state:stream has already been listened to,
Probably error is generated by stream that listening to Bluetooth characteristic.Whats the work around?
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'package:vibration/vibration.dart';
StreamSubscription _scanSubscription;
StreamSubscription _deviceConnection;
Stream<List<int>> stream;
List<double> traceDust = List();
const String CHAR_UUID = "AA:48:F8:CC:07:12";
const String Device_Name = "myDevice";
const String CHARACTERISTIC_UUID = "00000000-0111-1000-4000-000000000000";
BluetoothDeviceState _state;
Map<DeviceIdentifier, ScanResult> scanResults = new Map();
List<BluetoothService> services = new List();
BluetoothCharacteristic characteristic;
FlutterBlue flutterBlue = FlutterBlue.instance;
BluetoothDevice device;
class SearchScreen extends StatefulWidget {
#override
_SearchScreenState createState() => _SearchScreenState();
}
class _SearchScreenState extends State<SearchScreen> {
#override
void initState() {
super.initState();
_startScan();
}
#override
void dispose() {
super.dispose();
_stopScan();
_deviceConnection?.cancel();
_deviceConnection = null;
device.disconnect();
}
_startScan() {
_scanSubscription =
flutterBlue.scan(timeout: Duration(seconds: 4)).listen((scanResult) {
if (CHAR_UUID == scanResult.device.id.toString()) {
_stopScan();
_connect(scanResult.device);
print('connected');
}
}, onDone: _stopScan());
}
_stopScan() {
_scanSubscription?.cancel();
_scanSubscription = null;
}
_connect(BluetoothDevice d) async {
device = d;
await device.connect(autoConnect: true);
await device.discoverServices().then((value) {
setState(() {
services = value;
});
});
_turnOnCharacterService(services);
}
_turnOnCharacterService(List<BluetoothService> ser) async {
ser.forEach((service) {
service.characteristics.forEach((character) {
if (character.uuid.toString() == CHARACTERISTIC_UUID) {
character.setNotifyValue(!character.isNotifying);
setState(() {
stream = character.value;
});
}
});
});
}
String _dataParser(List<int> dataFromDevice) {
return utf8.decode(dataFromDevice);
}
vibrateOnAlert() async {
if (await Vibration.hasVibrator()) {
Vibration.vibrate(duration: 1000);
}
}
#override
Widget build(BuildContext context) {
return Container(
child: StreamBuilder<BluetoothDeviceState>(
stream: device.state,
initialData: BluetoothDeviceState.connecting,
builder: (context, snapshot) {
if (snapshot.data == BluetoothDeviceState.connected) {
return StreamBuilder<List<int>>(
stream: stream,
builder: (context, snapshot) {
var currentValue;
if (snapshot.hasError) {
return Text('Error');
}
if (snapshot.connectionState == ConnectionState.active) {
currentValue = _dataParser(snapshot.data);
traceDust.add(double.tryParse(currentValue) ?? 0);
if (currentValue.toString().compareTo('vibrate') == 0) {
vibrateOnAlert();
}
} else {
return Text('disconnected');
}
print('$currentValue');
return Text('connected');
});
}
return FlatButton(
color: Colors.white,
child: Text('reconnecct'),
onPressed: () {
setState(() {
flutterBlue.startScan(timeout: Duration(seconds: 2));
});
},
);
},
));
}
}
PS: Here flat button does nothing.Since connection state is a streambuilder it automatically reconnects and shows error.

Related

How can I access a stream from a BLE device in different routes in flutter

An ESP32 automatically connects to the app and after the connection, the app receives two streams from the ESP32. These two ESP32 streams are used as streams in the app and should be accessible via different routes in the app. How can I access these two streams? When I display these streams in the current route, I have no problems. I have tried to use StreamProvider, but I have not figured out how to use it correctly for this streams.
Here are my lines of codes:
import 'dart:async';
import 'dart:convert';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'package:provider/provider.dart';
import 'Draw/line_view.dart';
import 'main_screen_ble.dart';
class BleInitializationScreen extends StatefulWidget {
const BleInitializationScreen({Key? key, }) : super(key: key);
static const String id = 'BleInitializationScreen';
#override
State<BleInitializationScreen> createState() =>
_BleInitializationScreenState();
}
class _BleInitializationScreenState extends State<BleInitializationScreen> {
FlutterBlue flutterBlue = FlutterBlue.instance;
late StreamSubscription<ScanResult> scanSubscription;
bool connected = false;
late BluetoothDevice targetDevice;
late BluetoothCharacteristic targetCharacteristicAlpha;
late BluetoothCharacteristic targetCharacteristicBeta;
Stream<List<int>>? **streamAlpha**;
Stream<List<int>>? **streamBeta**;
startScan() {
scanSubscription = flutterBlue.scan().listen((scanResult) {
if (scanResult.device.name == TARGET_DEVICE_NAME) {
stopScan();
targetDevice = scanResult.device;
connectToDevice();
}
}, onDone: () => stopScan());
}
stopScan() {
scanSubscription.cancel();
}
connectToDevice() async {
if (targetDevice == null) return;
await targetDevice.connect();
connected = true;
discoverServices();
}
disconnectFromDevice() {
if (targetDevice == null) return;
targetDevice.disconnect();
}
discoverServices() async{
if (targetDevice == null) return;
List<BluetoothService> services = await targetDevice.discoverServices();
for (var service in services) {
if (service.uuid.toString() == SERVICE_UUID) {
for (var characteristic in service.characteristics) {
if (characteristic.uuid.toString() ==
CHARACTERISTIC_UUID_ANGLE_ALPHA) {
targetCharacteristicAlpha = characteristic;
}
if (targetCharacteristicAlpha.uuid.toString() ==
CHARACTERISTIC_UUID_ANGLE_ALPHA) {
targetCharacteristicAlpha
.setNotifyValue(!targetCharacteristicAlpha.isNotifying);
**streamAlpha** = targetCharacteristicAlpha.value;
}
}
for (var characteristicBeta in service.characteristics) {
if (characteristicBeta.uuid.toString() ==
CHARACTERISTIC_UUID_ANGLE_BETA) {
targetCharacteristicBeta = characteristicBeta;
targetCharacteristicBeta
.setNotifyValue(!targetCharacteristicBeta.isNotifying);
**streamBeta** = targetCharacteristicBeta.value;
}
}
}
}
}
#override
void initState() {
super.initState();
startScan();
Timer.periodic(const Duration(milliseconds: 5), (_timer){
if (connected){
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => const MainScreenBLE(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
var begin = const Offset(0.0, 0.0);
var end = Offset.zero;
var curve = Curves.ease;
var tween = Tween(begin: begin, end: end);
var curvedAnimation = CurvedAnimation(parent: animation, curve: curve);
return SlideTransition(
position: tween.animate(curvedAnimation),
child: child,
);
},
),
);
_timer.cancel();
}
});
}
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
StreamProvider<List<int>>(
create: (_) => streamAlpha,
initialData: []
),
],
child: Scaffold(
backgroundColor: kPrimaryColorRoseLight,
body: Builder(
builder: (BuildContext newContext) {
return SafeArea(
child: Stack(
children: [
//some childrens
],
),
);
}
),
),
);
}
}

WebView showing ERR_CONNECTION_REFUSED (using flutter)

I got this error while using webview in flutter.
error: ERR_CONNECTION_REFUSED
Here is my code:
import 'dart:async'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; import 'package:flutter/services.dart'; import 'package:mime/mime.dart'; import 'package:provider/provider.dart';
import 'package:num_plus_plus/src/backend/mathmodel.dart'; import 'package:num_plus_plus/src/pages/settingpage.dart';
class Server { class from inAppBrowser
late HttpServer _server;
int _port = 8080;
Server({int port = 8080}) { this._port = port; }
///Closes the server. Future<void> close() async { if (this._server != null) { await this._server.close(force: true); print('Server running on http://localhost:$_port closed'); this._server = null as HttpServer;
} }
Future<void> start() async { if (this._server != null) { throw Exception('Server already started on http://localhost:$_port');
}
var completer = new Completer(); runZoned(() { HttpServer.bind('127.0.0.1', _port, shared: true).then((server) { print('Server running on http://localhost:' + _port.toString());
this._server = server;
server.listen((HttpRequest request) async { var body = <int>[]; var path = request.requestedUri.path; path = (path.startsWith('/')) ? path.substring(1) : path; path += (path.endsWith('/')) ? 'index.html' : '';
try { body = (await rootBundle.load(path)).buffer.asUint8List(); catch (e) { print(e.toString()); request.response.close(); return;
}
var contentType = ['text', 'html']; if (!request.requestedUri.path.endsWith('/') && request.requestedUri.pathSegments.isNotEmpty) { var mimeType = lookupMimeType(request.requestedUri.path, headerBytes: body); if (mimeType != null) { contentType = mimeType.split('/');
}
}
request.response.headers.contentType = new ContentType(contentType[0], contentType[1], charset: 'utf-8'); request.response.add(body); request.response.close();
});
completer.complete();
}); onError: (e, stackTrace) => print('Error: $e $stackTrace'));
return completer.future; } }
class MathBox extends StatelessWidget { #override Widget build(BuildContext context) { final mathBoxController = Provider.of<MathBoxController>(context, listen: false); final mathModel = Provider.of<MathModel>(context, listen: false); final matrixModel = Provider.of<MatrixModel>(context, listen: false); final functionModel = Provider.of<FunctionModel>(context, listen: false); final mode = Provider.of<CalculationMode>(context, listen: false); return Stack( children: <Widget>[ WebView( onWebViewCreated: (controller) { controller.loadUrl("http://localhost:8080/assets/html/homepage.html"); mathBoxController.webViewController = controller;
}, onPageFinished: (s) { final setting = Provider.of<SettingModel>(context, listen: false); if (setting.initPage == 1) { mathBoxController.addExpression('\\\\bmatrix');
}
}, javascriptMode: JavascriptMode.unrestricted, javascriptChannels: Set.from([ JavascriptChannel( name: 'latexString', onMessageReceived: (JavascriptMessage message) { if (mode.value == Mode.Matrix) { matrixModel.updateExpression(message.message); else { if (message.message.contains(RegExp('x|y'))) { mode.changeMode(Mode.Function); functionModel.updateExpression(message.message); else { mode.changeMode(Mode.Basic); mathModel.updateExpression(message.message); mathModel.calcNumber();
}
}
}
), JavascriptChannel( name: 'clearable', onMessageReceived: (JavascriptMessage message) { mathModel.changeClearable(message.message == 'false'?false:true);
}
),
]),
), ClearAnimation(),
],
); } }
class ClearAnimation extends StatefulWidget { #override
_ClearAnimationState createState() => _ClearAnimationState(); }
class _ClearAnimationState extends State<ClearAnimation> with TickerProviderStateMixin {
late AnimationController animationController; late Animation animation;
#override void initState() { super.initState(); animationController = AnimationController(duration: const Duration(milliseconds: 500),vsync: this); final curve = CurvedAnimation(parent: animationController, curve: Curves.easeInOutCubic); animation = Tween<double>(begin: 0, end: 2000).animate(curve); Provider.of<MathBoxController>(context, listen: false).clearAnimationController = animationController; }
#override void dispose() { animationController.dispose(); super.dispose(); }
Widget _buildAnimation(BuildContext context, Widget? child) { return Positioned( top: 10-animation.value/2 as double, right: -animation.value/2, child: ClipOval( child: Container( height: animation.value, width: animation.value, color: Colors.blue[100],
),
),
); }
#override Widget build(BuildContext context) { return AnimatedBuilder( builder: _buildAnimation, animation: animation,
); } }
class MathBoxController {
late WebViewController _webViewController; late AnimationController clearAnimationController;
set webViewController(WebViewController controller) { this._webViewController = controller; }
void addExpression(String msg, {bool isOperator = false}) { assert(_webViewController != null);
_webViewController.evaluateJavascript("addCmd('$msg', {isOperator: ${isOperator.toString()}})"); }
void addString(String msg) { assert(_webViewController != null);
_webViewController.evaluateJavascript("addString('$msg')"); }
void equal() { assert(_webViewController != null);
_webViewController.evaluateJavascript("equal()"); }
void addKey(String key) { assert(_webViewController != null);
_webViewController.evaluateJavascript("simulateKey('$key')"); }
void deleteExpression() { assert(_webViewController != null);
_webViewController.evaluateJavascript("delString()"); }
void deleteAllExpression() { assert(_webViewController != null);
_webViewController.evaluateJavascript("delAll()"); }
}

Read data from Flutter via Bluetooth

I am trying to get the data from my bluetooth device. My problem is with the Flutter code to get such data.
services/sensor.dart
import 'dart:async';
import 'dart:convert' show utf8;
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'package:minertti/main.dart';
class SensorPage extends StatefulWidget {
const SensorPage({Key? key, required this.device}) : super(key: key);
final BluetoothDevice device;
#override
_SensorPageState createState() => _SensorPageState();
}
class _SensorPageState extends State<SensorPage> {
String service_uuid = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E";
String charaCteristic_uuid = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E";
late bool isReady;
late Stream<List<int>> stream;
late List _temphumidata;
double _charge = 0;
double _data_1 = 0;
double _data_2 = 0;
#override
void initState() {
super.initState();
super.initState();
isReady = false;
connectToDevice();
}
void dispose() {
widget.device.disconnect();
super.dispose();
}
connectToDevice() async {
if (widget.device == null) {
_pop();
return;
}
new Timer(const Duration(seconds: 15), () {
if (!isReady) {
disconnectFromDevice();
_pop();
}
});
await widget.device.connect();
discoverServices();
}
disconnectFromDevice() {
if (widget.device == null) {
_pop();
return;
}
widget.device.disconnect();
}
discoverServices() async {
if (widget.device == null) {
_pop();
return;
}
List<BluetoothService> services = await widget.device.discoverServices();
services.forEach((service) {
if (service.uuid.toString().isNotEmpty) {
service.characteristics.forEach((characteristic) {
if (characteristic.uuid.toString().isNotEmpty) {
characteristic.setNotifyValue(!characteristic.isNotifying);
stream = characteristic.value;
setState(() {
isReady = true;
});
}
});
}
});
if (!isReady) {
_pop();
}
}
_pop() {
Navigator.of(context).pop(true);
}
String _dataParser(List<int> dataFromDevice) {
return utf8.decode(dataFromDevice);
}
#override
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(
// title: Text('dht11 Sensor'),
// ),
body: Container(
child: !isReady
? Center(
child: Text(
"Waiting...",
style: TextStyle(
fontSize: 24, color: Color.fromARGB(255, 0, 0, 0)),
),
)
: Container(
child: StreamBuilder<List<int>>(
stream: stream,
builder: (BuildContext context,
AsyncSnapshot<List<int>> snapshot) {
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
if (snapshot.connectionState == ConnectionState.active) {
var data = snapshot.data as List<int>;
var currentValue = _dataParser(data);
print("REALDATA: $data");
_temphumidata = currentValue.split(",");
//_charge = double.parse('${_temphumidata[0]}');
//_data_1 = double.parse('${_temphumidata[1]}');
//_data_2 = _temphumidata[2];
return DeviceScreen1(
device: widget.device,
//charge: _charge,
//data_2: _data_2,
//data_1: _data_1,
charge: 90,
data_1: "Data 1",
data_2: "Data 2");
} else {
return Text('Check the stream');
}
},
),
)),
);
}
}
var data = snapshot.data as List;
var currentValue = _dataParser(data);
They do not show values. But, from my Arduino I know that it does send/notify data. That is, my problem is with reading and obtaining said data.

How to get value from map on another page in flutter

So I have a listview with which I used Future to fetch data and it displays fine. Now am trying to parse the value on the clicked item from the listview page to another page that will show details of the item click. Please how do I achieve this?
The Future
List dealData = List();
Future<String> _fetchComment() async {
setState(() {
isLoading = true;
debugPrint("emirate state");
});
try {
debugPrint("emirate try");
final result = await InternetAddress.lookup('google.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
print('connected');
debugPrint("emirate connect");
String url;
debugPrint("my select:$_mySelection");
if (_mySelection == null && _myFeatureSelection == null) {
url = "my rest api";
} else if (_myFeatureSelection != null) {
url =
"my rest api";
_mySelection = null;
} else if (_mySelection != null && _myFeatureSelection == null) {
url = "my rest api";
}
print("our url:$url");
var res = await http
.get(Uri.encodeFull(url), headers: {"Accept": "application/json"});
var resBody = json.decode(res.body);
debugPrint("emirate url:$url");
setState(() {
dealData = resBody;
isLoading = false;
});
print(resBody);
debugPrint("emirate:$resBody");
return "Sucess";
} else {
throw Exception('Failed to load profile');
}
} on SocketException catch (_) {
print('not connected');
setState(() => isLoading = false);
Navigator.popUntil(
context, (_) => !Navigator.canPop(context));
Navigator.pushReplacement(
context,
new MaterialPageRoute(
builder: (BuildContext context) => NoInternet()));
}
}
My listview and onclick
dealData
.map(
(position) => FutureBuilder<String>(
future: getDistance(
position["lat"],
position["lng"])
.then((value) =>
value.toString()),
builder: (context, snapshot) {
double myrate = double.parse(
position["ratings"] ==
null
? "0"
: position["ratings"]);
return Container(
child:Card(child:
GestureDetector(
onTap: () {
print(position); // position printed here
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext ctx) => Maps(position)));
},
).toList(),
My Map Class
class mapsFinal extends StatefulWidget {
final int position;
const mapsFinal(this.position);
#override
_MapsState createState() => _MapsState ();
}
class _MapsState extends State<mapsFinal> {
Widget build(BuildContext context) {
return Text("title" + widget.position.toString());
}
}
Please I need a second page that will display the item I clicked on here.
This is the simplest example of passing a value to a widget called "Maps":
// BOILERPLATE CODE TO MAKE THE EXAMPLE RUN
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Maps("THE VALUE"),
),
),
);
}
}
// THIS IS THE CLASS YOU NEED TO LOOK AT:
class Maps extends StatefulWidget {
final String position;
const Maps(this.position);
#override
_MapsState createState() => _MapsState ();
}
class _MapsState extends State<Maps> {
Widget build(BuildContext context) {
return Text("You passed: " + widget.position);
}
}

Flutter avoid multiple running FutureBuilder

in my simple code as new screen, unfortunately FutureBuilder work and get data from method twice!!
i'm not sure whats problem and how can i avoid that
class LessonDetail extends StatefulWidget {
final String monthKey;
final String lessonFileKey;
LessonDetail({#required this.monthKey, #required this.lessonFileKey});
#override
State<StatefulWidget> createState() {
return _LessonDetailState(monthKey, lessonFileKey);
}
}
class _LessonDetailState extends BaseState<LessonDetail> {
String monthKey;
String lessonFileKey;
_LessonDetailState(this.monthKey, this.lessonFileKey);
#override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
body: FutureBuilder(
future: _getLessonDetail(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
PlayLessonResponse response = snapshot.data;
print(response);
}
return Center(
child: CircularProgressIndicator(),
);
}),
),
);
}
Future<PlayLessonResponse> _getLessonDetail() async {
AudioList audioList = AudioList(
'http://www.sample.com',
'aaaaa'
);
List<AudioList> lst = [audioList,audioList,audioList];
PlayLessonResponse response = PlayLessonResponse(
2,
'',
'http://www.sample.com',
'2',
lst,
1,
'ssss'
);
print('++++++++++++++++++++');
return response;
}
}
BaseState class content:
abstract class BaseState<T extends StatefulWidget> extends State {
final Connectivity _connectivity = Connectivity();
StreamSubscription<ConnectivityResult> _connectivitySubscription;
bool isOnline = true;
Future<void> initConnectivity() async {
try {
await _connectivity.checkConnectivity();
} on PlatformException catch (e) {
print(e.toString());
}
if (!mounted) {
return;
}
await _updateConnectionStatus().then((bool isConnected){
if(mounted){
setState(() {
isOnline = isConnected;
});
}
});
}
#override
void initState() {
super.initState();
initConnectivity();
_connectivitySubscription = Connectivity()
.onConnectivityChanged
.listen((ConnectivityResult result) async {
await _updateConnectionStatus().then((bool isConnected){
if(mounted){
setState(() {
isOnline = isConnected;
});
}
});
});
}
#override
void dispose() {
_connectivitySubscription.cancel();
super.dispose();
}
Future<bool> _updateConnectionStatus() async {
bool isConnected;
try {
final List<InternetAddress> result =
await InternetAddress.lookup('google.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
isConnected = true;
}
} on SocketException catch (_) {
isConnected = false;
return false;
}
return isConnected;
}
}
output:
I/flutter (32289): ++++++++++++++++++++
I/flutter (32289): ++++++++++++++++++++
Just like what #Ricardo said, you shouldn't call the function directly inside the FutureBuilder's future method.
Instead, you should 1st run your function in init state, and store the response in a new variable. Only then assign variable to the future of FutureBuilder.
Code Example:
class LessonDetail extends StatefulWidget {
final String monthKey;
final String lessonFileKey;
LessonDetail({#required this.monthKey, #required this.lessonFileKey});
#override
State<StatefulWidget> createState() {
return _LessonDetailState(monthKey, lessonFileKey);
}
}
class _LessonDetailState extends BaseState<LessonDetail> {
String monthKey;
String lessonFileKey;
Future<PlayLesssonResponse> _myResponse; //added this line
_LessonDetailState(this.monthKey, this.lessonFileKey);
#override
void initState() {
_myResponse = _getLessonDetail(); // added this line
super.initState();
}
#override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
body: FutureBuilder(
future: _myResponse, //use _myResponse variable here
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
PlayLessonResponse response = snapshot.data;
print(response);
}
return Center(
child: CircularProgressIndicator(),
);
}),
),
);
}
Future<PlayLessonResponse> _getLessonDetail() async {
AudioList audioList = AudioList(
'http://www.sample.com',
'aaaaa'
);
List<AudioList> lst = [audioList,audioList,audioList];
PlayLessonResponse response = PlayLessonResponse(
2,
'',
'http://www.sample.com',
'2',
lst,
1,
'ssss'
);
print('++++++++++++++++++++');
return response;
}
}