반응형

Flutter 의 Pull To Refresh는 RefreshIndicator를 사용한다.

onRefresh 속성에 Pull 동작시 콜백되는 함수를 정의한다.

child: RefreshIndicator(
    onRefresh: _startScan,
    child: ListView.builder(
      itemCount: _foundBleUARTDevices.length,
      itemBuilder: (BuildContext context, int index) => Card(
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(16.0),
        ),
        elevation: 4.0,
        child: ListTile(
          dense: true,
          enabled: !(!_connected && _scanning),
          trailing: GestureDetector(
            behavior: HitTestBehavior.translucent,
            onTap: () {
              //(!_connected && _scanning) || (!_scanning && _connected)? (){}: onConnectDevice(index);
            },
            child: Container(
              width: 48,
              height: 48,
              padding: const EdgeInsets.symmetric(vertical: 4.0),
              alignment: Alignment.center,
              child: const Icon(Icons.add_link),
            ),
          ),
          subtitle: Text(_foundBleUARTDevices[index].id),
          title: Text("${_foundBleUARTDevices[index].name}",style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20)),
        ),
      ),
    ),
  ),

콜백되는 함수에서 Refresh 처리를 수행하도록 하며,

콜백되는 함수는 Future 를 return하도록 작성되어야 한다.

Future<void> _startScan() async {
    if (Platform.isAndroid) {
      if(await checkIfPermissionGranted()) {
        if(_scanning==true) {
          _stopScan();
        }
          _foundBleUARTDevices = [];
          _scanning = true;
          setState(() {});
          _scanStream = flutterReactiveBle.scanForDevices(
            withServices: [_UART_UUID]).listen((device) {
            //withServices: []).listen((device) {
              if (_foundBleUARTDevices.every((element) => element.id != device.id)) {
                if(device.name.contains('ACRO')) {
                  _foundBleUARTDevices.add(device);
                  print("${device.name}\n");
                  setState(() {});
                }
              }
            }, onError: (Object error) {
              print("${_logTexts}ERROR while scanning:$error \n");
            }
          );
      } else {
        await showNoPermissionDialog();
        openAppSettings();
      }
    }
    return Future<void>.value();
  }
반응형

'Flutter > 기본' 카테고리의 다른 글

[Flutter / 기본] ListTile  (0) 2022.07.19
[Flutter / 기본] Card  (0) 2022.07.18
[Flutter / 기본] ListView  (0) 2022.07.18
[Flutter / 기본] SingleChildScrollView  (0) 2022.07.14
[Flutter / 기본] 팝업 메세지 - showDialog, AlertDialog  (0) 2022.07.13
반응형

ListTile은 주로 ListView 의 각 항목을 표현하는

Text나 Icon 등을 나열하는데 사용된다.

child: ListView.builder(
    itemCount: _foundBleUARTDevices.length,
    itemBuilder: (BuildContext context, int index) => Card(
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(16.0),
      ),
      elevation: 4.0,
      child: ListTile(
        dense: true,
        enabled: !(!_connected && _scanning),
        trailing: GestureDetector(
          behavior: HitTestBehavior.translucent,
          onTap: () {
            //(!_connected && _scanning) || (!_scanning && _connected)? (){}: onConnectDevice(index);
          },
          child: Container(
            width: 48,
            height: 48,
            padding: const EdgeInsets.symmetric(vertical: 4.0),
            alignment: Alignment.center,
            child: const Icon(Icons.add_link),
          ),
        ),
        subtitle: Text(_foundBleUARTDevices[index].id),
        title: Text("${_foundBleUARTDevices[index].name}",style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: Colors.black)),
      ),
    ),
  ),

- dense 속성은 위젯의 밀집 여부를 설정한다. true 또는 false값을 사용하며,

  true로 설정할 경우 해당 ListTile의 높이와 내부의 Text나 icon을 포함한 약간씩 작아진다.

- enabled은 ListTile이 상호작용을 하는 즉, 대화형인지 설정한다.

  true 또는 false 값을 사용하며, false인 경우 ListTile은 현재 ListTile 테마의

  비활성화된 색상으로 스타일이 지정되고, onTap 및 onLongPress 콜백이 작동하지 않는다.

- leading은 ListTile의 앞부분에 배치할 위젯을 설정한다.

- trailing은 ListTile의 뒷부분에 배치할 위젯을 설정한다.

- title은 ListTile의 주요 내용으로 leading의 내용과 trailing 내용 중간에 배치되는 위젯이다.

- subtitle은 title의 아래에 작게 표현되는 위젯이다.

- onTap 사용자가 ListTile을 터치했을 때 호출되는 콜백을 정의한다.

  (위에서 예를 든 onTap은 ListTile을 탭했을 때 수행되는 것이 아니라,

    trailing을 탭했을 때 수행된다.)

반응형
반응형

카드 형태를 표현하기 위한 위젯이다.

보통 ListView나 GridView와 같은 위젯으로 감싸서 사용된다.

주로 사용하는 속성은 shape 속성과 elevation 속성이 있다.

child: ListView.builder(
  itemCount: _foundBleUARTDevices.length,
  itemBuilder: (BuildContext context, int index) => Card(
    shape: RoundedRectangleBorder(	//모서리를 둥글게 설정
      borderRadius: BorderRadius.circular(16.0),
    ),
    elevation: 4.0,	//그림자의 깊이를 설정
    child: ListTile(
      dense: true,
      enabled: !(!_connected && _scanning),
      trailing: GestureDetector(
        behavior: HitTestBehavior.translucent,
        onTap: () {
          //(!_connected && _scanning) || (!_scanning && _connected)? (){}: onConnectDevice(index);
        },
        child: Container(
          width: 48,
          height: 48,
          padding: const EdgeInsets.symmetric(vertical: 4.0),
          alignment: Alignment.center,
          child: const Icon(Icons.add_link),
        ),
      ),
      subtitle: Text(_foundBleUARTDevices[index].id),
      title: Text("${_foundBleUARTDevices[index].name}",style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: Colors.black)),
    ),
  ),
),
반응형
반응형

ListView 는 가장 일반적으로 사용되는 스크롤 위젯이며,

child는 ListView 의 내용을 채우는데 사용된다.

  • ListView : 일반적인 ListView를 명시적으로 호출하고 children을 전달하는 방법
  • ListView.builder : builder를 사용하여 동적으로 item을 추가하는 방법
  • ListView.separated : ListView.builder에서 item을 좀 더 명확하게 구분해서 보여주는 방법

1. ListView

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text(widget.title),
    ),
    body: Center(
      child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Expanded(
          child: ListView(
            padding: const EdgeInsets.all(8),
            children: <Widget>[
              Container(
                height: 50,
                color: Colors.amber[100],
                child:const Center(child: Text('stage 1')),
              ),
              Container(
                height: 50,
                color: Colors.amber[50],
                child:const Center(child: Text('stage 2')),
              ),
            ]),
          ),
      ]),
    ),  
  );
}

위의 예제는 Expanded로 감싸지 않으면, 오류가 난다.

ListView가 가질 수 있는 공간을 알수 없기 때문에 남는 공간을

Expanded에게 준다는 뜻을 명시적으로 해두어야 한다.

Expanded에 대해서는 차후 좀 더 알아보도록 한다.

 

2. ListView.builder

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text(widget.title),
    ),
    body: SingleChildScrollView(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Container(
            margin: EdgeInsets.all(30),
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(10),
              border: Border.all(
                  color: Colors.blue,
                  width:2
              )
            ),
            height: MediaQuery.of(context).size.height*0.8,
            child: ListView.builder(
              itemCount: _foundBleUARTDevices.length,
              itemBuilder: (BuildContext context, int index) => Card(
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(16.0),
                ),
                elevation: 4.0,
                child: ListTile(
                  dense: true,
                  enabled: !(!_connected && _scanning),
                  trailing: GestureDetector(
                    behavior: HitTestBehavior.translucent,
                    onTap: () {
                      //(!_connected && _scanning) || (!_scanning && _connected)? (){}: onConnectDevice(index);
                    },
                    child: Container(
                      width: 48,
                      height: 48,
                      padding: const EdgeInsets.symmetric(vertical: 4.0),
                      alignment: Alignment.center,
                      child: const Icon(Icons.add_link),
                    ),
                  ),
                  subtitle: Text(_foundBleUARTDevices[index].id),
                  title: Text("${_foundBleUARTDevices[index].name}",style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: Colors.black)),
                ),
              ),
            ),
          ),
        ],
      ),
    ),
  );
}

2022.07.07 - [Flutter/기능 구현] - [Flutter / BLE] 기능 구현 - SCAN 에서 사용된 ListView.builder의 예이다.

itemBuilder를 사용하여 item을 itemCount에 맞춰서 ListView를 구성하면 된다.

BLE Device Scan과 같이 BLE Device(item)가 검색될 때마다 호출하여 리스트를 추가한다.

 

3. ListView.separated

child: ListView.separated(
  separatorBuilder: (BuildContext context, int index) => const Divider(
    height: 10,
    color: Colors.blue,
  ),
  itemCount: _foundBleUARTDevices.length,
  itemBuilder: (BuildContext context, int index) => Card(
      shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(16.0),
    ),
    elevation: 4.0,
    child: ListTile(
      dense: true,
      enabled: !(!_connected && _scanning),
      trailing: GestureDetector(
        behavior: HitTestBehavior.translucent,
        onTap: () {
          //(!_connected && _scanning) || (!_scanning && _connected)? (){}: onConnectDevice(index);
        },
        child: Container(
          width: 48,
          height: 48,
          padding: const EdgeInsets.symmetric(vertical: 4.0),
          alignment: Alignment.center,
          child: const Icon(Icons.add_link),
        ),
      ),
      subtitle: Text(_foundBleUARTDevices[index].id),
      title: Text("${_foundBleUARTDevices[index].name}",style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: Colors.black)),
    ),
  ),
),

ListView.separated는 ListView.builder에 구분선이 추가된 것이다.

사용법은 ListView.builder와 동일하며, 추가적으로 separatorBuilder 속성이 추가되어

구분선에 대한 정의를 해준다.

반응형
반응형

SingleChildScrollView 는 Row나 Column 위젯과 같이 쓴다.

Column 이나 Row에 나열하는 위젯이 많아져서 화면에 모두 출력되지 않을 때

SingleChildScrollView로 감싸주면 스크롤이 가능해진다.

Column이나 Row 위젯 외에도 단 한개의 위젯이 너무 커서 화면에 

모두 나타나지 않을 때에도 사용이 가능하다.

body: SingleChildScrollView(
        child: Column(
          children: [
          ...
          ],
        ),
     ),

 

반응형
반응형

사용자에게 상태를 알리거나 동작을 물어보는 용도로 사용되는 팝업 메세지를 구현하는 방법으로

AlertDialog가 있다.

- AlertDialog는 Title, Content, Actions영역으로 구분되며, 세 항목은 필수 항목은 아니다.

- AlertDialog는 showDialog라는 함수를 통하여 화면에 출력된다.

- Dialog를 닫을 때에는 Navigator.pop(context); 를 호출하여 이전 화면이 출력되도록 한다.

- 기본적으로 Dialog의 바깥 화면을 터치하면 Navigator.pop(context); 를 수행한 것처럼 Dialog가 닫힌다.

  이것을 방지하기 위해 showDialog의 barrierDismissible의 속성값을 false로 지정하면,

  Dialog 바깥 화면이 터치되지 않는다.

- Dialog의 모서리는 AlertDialog의 shape 속성에 RoundedRectangleBorder를 추가하면 된다.

 

 

void _showDialog() { 
    showDialog(
      context: context,
      barrierDismissible: false, 
      builder: (BuildContext context) { 
        return AlertDialog(
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(8.0)
          ),
          title: new Text("Alert Dialog title"),
          content: SingleChildScrollView(child:new Text("Alert Dialog body")),
          actions: <Widget>[ 
            new FlatButton(
              child: new Text("Close"),
              onPressed: () {
                Navigator.pop(context);
              },
            ),
          ],
        );
      },
    );
  }

 

반응형

+ Recent posts