Flutter: checkbox в приложении

Для того чтобы получить отображение чекбокса вида:

Необходимо использовать виджет Checkbox. Положим его в контейнер, а контейнер разделим на две части: в первой будет текст, а во второй чекбокс:

                      return Container(
                        padding: EdgeInsets.symmetric(vertical: 1),
                        child:
                          Row(
                              mainAxisAlignment: MainAxisAlignment.spaceBetween,
                              children:<Widget>[
                                Expanded(
                                  flex: 2, // 60%
                                  child: Column(
                                    crossAxisAlignment: CrossAxisAlignment.start,
                                    children: [
                                      Align(
                                        alignment: AlignmentDirectional(-1, 0),
                                        child:
                                        Text("Какой то чекбокс", style: TextStyle(fontSize: 22)),
                                      ),
                                    ],
                                  )
                                ),
                                Expanded(
                                flex: 2, // 60%
                                child:
                                  Checkbox(
                                      value: true,
                                      onChanged: (bool? value) { // здесь мы можем обновить какую то переменную после выбора
                                        setState(() {
                                          
                                        });
                                      }
                                  )
                                )
                              ]
                          )
                        );

Табы в приложении

Одним из вариантов организации меню в приложении, является организация «табов», которые могут располагаться как внизу экрана, так и вверху. Например что-то вроде:

Это изображение имеет пустой атрибут alt; его имя файла - %D0%B8%D0%B7%D0%BE%D0%B1%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5-2.png

Для начала создадим отдельную функцию отрисовки табов:

class _MainPageState extends State<MainPage> {// _ впереди класса, означает чтоб скрыть доступ из другх файлов
  final scaffoldKey = GlobalKey<ScaffoldState>();

  TabBar createTabBar()  {
    return TabBar(
      tabs: [
        Row (children: [Icon(Icons.ac_unit), SizedBox(width:5), Text("Датчики")]),
        Row (children: [Icon(Icons.adjust), SizedBox(width:5), Text("Реле")]),
        Row (children: [Icon(Icons.add_to_queue), SizedBox(width:5), Text("Свет")]),
        Row (children: [Icon(Icons.accessibility_sharp), SizedBox(width:5), Text("Комфорт")]),
      ],
      isScrollable: true,
    );
  }

isScrollable — отвечает за плавное переключение табов. Если эффект анимации не нужен — можно отключить.

Далее главный виджет:

@override
  Widget build(BuildContext context) {
    EdgeInsets a2; EdgeInsetsDirectional a;
    return DefaultTabController(
      length: 4, // количество табов
      child:Scaffold(
          appBar: AppBar(
            bottom: createTabBar(), // функция - построитель табов
            title: Text("Усадьба Мальгино"),
          ),
          body: TabBarView(
              children: [
                Text("1"),
                Text("2"),
                Text("3"),
                Text("4"),
          ]
          )
      )
    );

При отрисовке в методе TabBar можно использовать следующие настройки:

  • isScrollable = true/false = плавный скроллинг
  • padding = отступы
  • indicatorColor = цвет подсветки
  • indicatorWeight = 2.0 = толщина подчёркивания
  • indicatorPadding = EdgeInsets.zero = отступы подчёркивания
  • indicatorSize = размер чёрточки
  • labelColor = цвет текста таба
  • labelStyle = стиль текста таба
  • labelPadding = отступы между метками
  • unselectedLabelColor = цвет не выделенного таба
  • unselectedLabelStyle = стиль не выделенного таба
  • onTap = действие при выборе

Диалоговое окно на чистом javascript

Задача: вывести диалоговое окно без использования Jquery

Решение:

<style>
.DialogConfirm{
    position: fixed;
    width: 100%;
    height: 100%;
    background: rgba(96, 79, 79, 0.51);
    top: 0px;
    left: 0px;
    right: 0px;
    bottom: 0px;
}

.DialogConfirm>div{
  width: 90%;
  margin: 30% auto;
  padding: 10px 20px;
  background: #f7f7f7;
  border-style: solid;
  border-color: aliceblue;
  border-width: initial;
}

.DialogConfirm div p{
  text-align:center;
}

.DialogConfirm div button{
    width: 40%;
    margin: 0 5%;
    border-style: double;
    border-color: black;
}    
</style>    
<script>
function myConfirm(text,buttons=Array(), cb){
    var body = document.getElementsByTagName('body')[0];
    var overlay = document.createElement('div');
    overlay.className = 'DialogConfirm';
    var box = document.createElement('div');  
    var p = document.createElement('p');
    p.appendChild(document.createTextNode(text));
    box.appendChild(p);
    buttons.forEach(function(bt, index, array) {
        tmp_btn=document.createElement('button');
        tmp_btn.appendChild(document.createTextNode(bt));
        tmp_btn.addEventListener('click', function(){ cb(bt); body.removeChild(overlay); }, false);
        box.appendChild(tmp_btn);
    });
        
    overlay.appendChild(box)
    body.appendChild(overlay);    
}
myConfirm('Бахнем и весь мир в труху?',["Давай","Попозжа"], function(value){ console.log(value); });   
</script>    

postgresql: выполнение запроса из командной строки

Задача: периодически необходимо подрезать логи

Решение: самое простое подготовить скрипт на bash и разместить ссылку на его выполнение в crontab:

#!/bin/bash
PGPASSWORD="Zewkkjfoeir" psql -U eee_pg -d eee -c "delete from log where dt<now() - INTERVAL '90 DAYS'"

Android: Не работает диалог выбора файла в webview

Задача: в контексте webview приложения для android, не открывается диалоговое окно выбора файла, в виду отсутствия в «базовом» варианте работы webview необходимых обработчиков. Решение описываемое в статье 2020года не работает в 2022 году

Решение: в конце 2022 года, рабочая минимальная обвязка для обработки выбора файлов такова:

public class MainActivity extends AppCompatActivity {
    private ValueCallback<Uri[]> fileChooserCallback;
    private WebView mbrowser;
    Uri home = Uri.parse("https://цукмсцумкцу.ru/choser.php");
    //загрузки изображений
    private ValueCallback<Uri> mUploadMessage;
    public ValueCallback<Uri[]> uploadMessage;
    public static final int REQUEST_SELECT_FILE = 100;
    public static final int REQUEST_QR_CODE = 200;
    private final static int FILECHOOSER_RESULTCODE = 1;
    public final static int CHILD_FINISH = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mbrowser=(WebView) findViewById(R.id.main_webview);

        mbrowser.getSettings().setJavaScriptEnabled(true); // разрешен javascript

        mbrowser.getSettings().setDatabaseEnabled(true); // хранение данных во встроенной БД в браузере
        mbrowser.getSettings().setDomStorageEnabled(true);
        mbrowser.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);   // разрешать открывать окна

        mbrowser.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView vw, WebResourceRequest request) {
                if (request.getUrl().toString().contains(home.getHost())) {
                    vw.loadUrl(request.getUrl().toString());
                } else {
                    Intent intent = new Intent(Intent.ACTION_VIEW, request.getUrl());
                    vw.getContext().startActivity(intent);
                }

                return true;
            }
        });

        mbrowser.setWebChromeClient(new WebChromeClient() {
            @Override
            public void onPermissionRequest(final PermissionRequest request) {
                request.grant(request.getResources());
            }

            @Override
            public boolean onShowFileChooser(WebView vw, ValueCallback<Uri[]> filePathCallback,
                                             FileChooserParams fileChooserParams) {
                if (fileChooserCallback != null) {
                    fileChooserCallback.onReceiveValue(null);
                }
                fileChooserCallback = filePathCallback;

                Intent selectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
                selectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
                selectionIntent.setType("*/*");

                Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
                chooserIntent.putExtra(Intent.EXTRA_INTENT, selectionIntent);
                startActivityForResult(chooserIntent, 0);

                return true;
            }
        });

        mbrowser.setOnKeyListener((v, keyCode, event) ->
        {
            WebView vw = (WebView) v;
            if (event.getAction() == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_BACK && vw.canGoBack()) {
                vw.goBack();

                return true;
            }

            return false;
        });
        mbrowser.setDownloadListener((uri, userAgent, contentDisposition, mimetype, contentLength) -> handleURI(uri));
        mbrowser.setOnLongClickListener(v -> {
            handleURI(((WebView) v).getHitTestResult().getExtra());

            return true;
        });

        mbrowser.loadUrl(home.toString());
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);

        fileChooserCallback.onReceiveValue(new Uri[]{Uri.parse(intent.getDataString())});
        fileChooserCallback = null;
    }

    private void handleURI(String uri) {
        if (uri != null) {
            Intent i = new Intent(Intent.ACTION_VIEW);
            i.setData(Uri.parse(uri.replaceFirst("^blob:", "")));

            startActivity(i);
        }
    }
}
1 58 59 60 61 62 308