#java #flutter #listview
Вопрос:
В настоящее время я разрабатываю приложение на Android Studio для включения и выключения грузовика. Приложение будет клиентом нашего сервера TCP, который будет отправлять данные о грузовике.
У меня есть проблема при реализации истории состояния грузовика. Прямо сейчас я могу отображать полученные данные в истории. Однако мы хотим, чтобы кнопки воспроизведения и остановки грузовика были недоступны, пока сервер не выпустил их с сообщением «разблокировано». Я начинаю реализовывать это с вызова логической переменной isButtonClickable
, инициализируемой значением true.
Я вставил условие в OnPressed{}
каждую из кнопок. Итак, я сталкиваюсь с проблемой, когда пытаюсь проанализировать сообщение, и я хочу посмотреть, равно ли оно «Заблокировать» или «Разблокировать», чтобы изменить состояние логической переменной.
Я попытался проверить входящие сообщения, когда я читаю и отображаю их в своем Listview.Builder
, но я получаю сообщение об ошибке, потому что его невозможно использовать SetState()
во время создания виджета приложением.
Вот как выглядит мой экран в данный момент:
Вот что я пытаюсь сделать :
import 'package:bubble/bubble.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:untitled/truck_icons.dart';
import '../models/message.dart';
import '../utils/validators.dart';
import '../tcp_bloc/tcp_bloc.dart';
import 'about_page.dart';
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
@override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
TcpBloc? _tcpBloc;
TextEditingController? _hostEditingController;
TextEditingController? _portEditingController;
TextEditingController? _chatTextEditingController;
bool isButtonClickable=true;
void ButtonUnclickable(){
setState(() {
isButtonClickable=false;
});
}
void ButtonClickable(){
setState(() {
isButtonClickable=true;
});
}
void TestMessage(TcpState tcpState,int idx){
Message m = tcpState.messages[idx];
if(m.message=="Unlock"){
ButtonClickable();
}else{
ButtonUnclickable();
}
}
@override
void initState() {
super.initState();
_tcpBloc = BlocProvider.of<TcpBloc>(context);
//Default parameter of my TCP server
_hostEditingController = new TextEditingController(text: '192.168.1.28');
_portEditingController = new TextEditingController(text: '9632');
_chatTextEditingController = new TextEditingController(text: '');
_chatTextEditingController!.addListener(() {
setState(() {
});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Truck Application')
),
body: BlocConsumer<TcpBloc, TcpState>(
bloc: _tcpBloc,
listener: (BuildContext context, TcpState tcpState) {
if (tcpState.connectionState == SocketConnectionState.Connected) {
ScaffoldMessenger.of(context)
..hideCurrentSnackBar();
} else if (tcpState.connectionState == SocketConnectionState.Failed) {
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(
content: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [Text("Connection failed"), Icon(Icons.error)],
),
backgroundColor: Colors.red,
),
);
}
},
builder: (context, tcpState) {
if (tcpState.connectionState == SocketConnectionState.None || tcpState.connectionState == SocketConnectionState.Failed) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8.0),
//Checking IP/port TCP serveur
child: ListView(
children: [
TextFormField(
controller: _hostEditingController,
autovalidateMode : AutovalidateMode.always,
validator: (str) => isValidHost(str) ? null : 'Invalid hostname',
decoration: InputDecoration(
helperText: 'The ip address or hostname of the TCP server',
hintText: 'Enter the address here, e. g. 10.0.2.2',
),
),
TextFormField(
controller: _portEditingController,
autovalidateMode : AutovalidateMode.always,
validator: (str) => isValidPort(str) ? null : 'Invalid port',
decoration: InputDecoration(
helperText: 'The port the TCP server is listening on',
hintText: 'Enter the port here, e. g. 8000',
),
),
ElevatedButton(
//Checking host and port before connection attempt
child: Text('Connect'),
onPressed: isValidHost(_hostEditingController!.text) amp;amp; isValidPort(_portEditingController!.text)
? () {
_tcpBloc!.add(
Connect(
host: _hostEditingController!.text,
port: int.parse(_portEditingController!.text)
)
);
}
: null,
)
],
),
);
} else if (tcpState.connectionState == SocketConnectionState.Connecting) {
//Connection attempt with abort button
return Center(
child: Column(
children: <Widget>[
CircularProgressIndicator(),
Text('Connecting...'),
ElevatedButton(
child: Text('Abort'),
onPressed: () {
_tcpBloc!.add(Disconnect());
},
)
],
),
);
} else if (tcpState.connectionState == SocketConnectionState.Connected) {
// Connection OK --> Screen Play and Stop truck
return Column(
children: [
Expanded(
child: Container(
padding: EdgeInsets.all(10),
margin: EdgeInsets.fromLTRB(20, 10, 20, 10),
//1 ligne
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
//Play button
Container(
child: Column(
children: [
Opacity(
opacity: isButtonClickable? 1.0:0.2,
//padding: EdgeInsets.all(5),
child: RaisedButton(
padding: EdgeInsetsDirectional.all(10),
onPressed: () {
if (isButtonClickable){
_tcpBloc!.add(SendMessage(message: '1'));
}
},
color: Colors.green,
child: Icon(
Icons.play_arrow_sharp,
color: Colors.white,
size: 30,
),
),
),
SizedBox(height: 5),
Text('Play')
],
),
),
//Stop Buttton
Container(
child: Column(
children: [
Opacity(
//padding: EdgeInsets.all(5),
opacity: isButtonClickable? 1.0:0.2,
child: RaisedButton(
padding: EdgeInsetsDirectional.all(10),
onPressed: () {
if(isButtonClickable){
_tcpBloc!.add(SendMessage(message: '0'));
}
},
color: Colors.red,
child: Icon(
Icons.stop,
color: Colors.white,
size: 30,
),
),
),
SizedBox(height: 5),
Text('Stop')
],
),
),
],
),
),
),
//Read and display received and requested data
Container (
child: Text("Historique de l'état du camion :",style: TextStyle(color: Colors.white, fontSize: 23)
),
decoration: ShapeDecoration(
color: Colors.lightBlue,
shape: RoundedRectangleBorder (
borderRadius: BorderRadius.circular(15.0),
side: BorderSide(
width: 5,
color: Colors.lightBlue),
),
),
padding: EdgeInsets.all(3),
margin: EdgeInsets.fromLTRB(10, 3, 10, 3),
),
Container(
height: 460,
width : 600,
decoration: BoxDecoration(color: Colors.white38,border: Border.all(width: 5,
color: Colors.lightBlue,
),),
child: ListView.builder(
itemCount: tcpState.messages.length,
itemBuilder: (context, idx) {
Message m = tcpState.messages[idx];
return Padding(
padding: const EdgeInsets.all(8.0),
child: Bubble(
child:
Text(m.message),
alignment: m.sender == Sender.Client ? Alignment.centerRight : Alignment.centerLeft,
),
);
}
),
),
//Disconnect Button
ElevatedButton(
child: Text('Disconnect'),
onPressed: () {
_tcpBloc!.add(Disconnect());
},
),
],
);
} else {
return Container();
}
},
),
);
}
}
Ответ №1:
Flutter позволяет проще справиться с вашей ситуацией: вместо того, чтобы проверять, работает ли ваша служба TCP «или» еще не запущена», вам нужно дождаться Future
ответа от вашего TCP-сервера и выполнить соответствующую сборку. Или, если ваша серверная служба «переключается» более одного раза (вкл.<->выкл.), вам следует подписаться на< -> Stream
, чтобы проверить изменение ее значения и выполнить соответствующую сборку.
Чтобы сделать это легко, я бы сказал, что вы должны окончательно проверить FutureBuilder
виджеты и StreamBuilder
виджеты, которые делают именно то, что вы просите.
Вот документация FutureBuilder.
Вот документация StreamBuilder.
В них также есть быстрое и приятное видео, чтобы вы могли быстрее понять, как его использовать (-: