Blog

  • Dart: Tạo packages.

    Ngôn ngữ Dart sử dụng các package để chia sẻ phần mềm như thư viện và công cụ. Bài này cho bạn biết cách tạo một package, tập trung vào loại package phổ biến nhất, package thư viện.

    Điều gì tạo nên một package thư viện?

    Sơ đồ sau đây cho thấy cách bố trí của package thư viện đơn giản nhất:

    Các yêu cầu tối thiểu đối với một thư viện là:

    – tệp pubspec

    Tệp pubspec.yaml cho một thư viện cũng giống như cho một package ứng dụng. Không có ký hiệu đặc biệt nào để chỉ ra rằng package đó là một thư viện.

    – thư mục lib

    Như bạn có thể mong đợi, mã thư viện nằm trong thư mục lib và công khai đối với các package khác. Bạn có thể tạo bất kỳ hệ thống phân cấp nào dưới lib, nếu cần. Theo quy ước, mã thực thi được đặt dưới lib / src. Mã dưới lib / src được coi là riêng tư; các package khác sẽ không bao giờ cần nhập src / …. Để đặt các API dưới lib / src ở chế độ công khai, bạn có thể xuất tệp lib / src từ tệp trực tiếp dưới lib.

    Cấu thành một package thư viện

    Package thư viện dễ bảo trì, mở rộng và kiểm tra nhất khi bạn tạo các thư viện nhỏ, riêng lẻ, được gọi là thư viện nhỏ. Trong hầu hết các trường hợp, mỗi lớp phải nằm trong thư viện nhỏ của riêng nó, trừ khi bạn gặp trường hợp hai lớp được kết hợp chặt chẽ với nhau.

    Tạo tệp thư viện “chính” trực tiếp dưới lib, lib / <package-name> .dart, xuất tất cả các API công khai. Điều này cho phép người dùng có được tất cả các chức năng của thư viện bằng cách nhập một tệp.

    Thư mục lib cũng có thể bao gồm các thư viện có thể nhập khác, không phải src,. Ví dụ: có thể thư viện chính của bạn hoạt động trên nhiều nền tảng, nhưng bạn tạo các thư viện riêng biệt dựa trên dart: io hoặc dart: html. Một số package có các thư viện riêng biệt được nhập với một tiền tố, trong khi thư viện chính thì không.

    Hãy xem cách tổ chức package thư viện trong thế giới thực: giá sách. Package giá sách cung cấp một cách dễ dàng để tạo máy chủ web bằng Dart và được bố trí theo cấu trúc thường được sử dụng cho các package thư viện Dart:

    Trực tiếp bên dưới lib, tệp thư viện chính, shelf.dart, xuất API từ một số tệp trong lib / src. Để tránh để lộ nhiều API hơn dự định – và để cung cấp cho nhà phát triển cái nhìn tổng quan về toàn bộ API công khai của package – chương trình sử dụng books.dart để chỉ định chính xác những ký hiệu nào cần xuất:

    Package shelf cũng chứa một thư viện nhỏ: shelf_io. Bộ điều hợp này xử lý các đối tượng HttpRequest từ dart: io.

    Nhập các tệp thư viện

    Khi nhập tệp thư viện từ một package khác, hãy sử dụng chỉ thị package: để chỉ định URI của tệp đó.

    Khi nhập tệp thư viện từ package của riêng bạn, hãy sử dụng một đường dẫn tương đối khi cả hai tệp nằm trong lib hoặc khi cả hai tệp nằm ngoài lib. Package sử dụng: khi tệp nhập nằm trong lib và tệp nhập ở bên ngoài.

    Hình dưới đây cho thấy cách nhập lib / foo / a.dart từ cả lib và web.

    Nhập và xuất các tệp thư viện có điều kiện.

    Nếu thư viện của bạn hỗ trợ nhiều nền tảng, thì bạn có thể cần nhập hoặc xuất các tệp thư viện có điều kiện. Một trường hợp sử dụng phổ biến là một thư viện hỗ trợ cả nền tảng web và nền tảng gốc.

    Để nhập hoặc xuất có điều kiện, bạn cần kiểm tra sự hiện diện của các thư viện dart: *. Dưới đây là một ví dụ về mã xuất có điều kiện để kiểm tra sự hiện diện của dart: io và dart: html:

    Đây là những gì mã đó làm:

    • Trong một ứng dụng có thể sử dụng dart: io (ví dụ: một ứng dụng dòng lệnh), hãy xuất src / hw_io.dart.
    • Trong một ứng dụng có thể sử dụng dart: html (một ứng dụng web), hãy xuất src / hw_html.dart.
    • Nếu không, hãy xuất src / hw_none.dart.

    Để nhập tệp có điều kiện, hãy sử dụng mã tương tự như trên, nhưng thay đổi export thành import.

    Trên đây là các chia sẽ của mình về tạo package trong ngôn ngữ dart.

    Cảm ơn mọi người đã theo dõi.

    Author: DuongVT19

  • Custom Paint trong Flutter

    Lớp con CustomPainter ghi đè hai phương thức: paint()và shouldRepaint().

    Hãy để tôi hướng dẫn bạn qua mã để hiểu rõ hơn về những gì đang xảy ra ở đây. Như bạn có thể thấy giao diện người dùng mà chúng ta sẽ đạt được theo đó, tôi đã tạo một lớp CustomPaintExample chứa mã cho giao diện người dùng. Có hai yếu tố chính, hình dạng và hoạt ảnh của nút. Nút khi được nhấn sẽ tăng giá trị percentValue lên 10 và theo đó có thể thấy tiến trình trên đường viền nút đang tăng lên. Tiện ích này cũng được chia thành hai phần tử CustomPainterExample.buildShape() và CustomPainterExample.buildButtonAnimation() tôi đã tạo trong các lớp khác nhau CustomPainterExampleWidgets.

    Trong Class CustomPainterExampleWidgets, Phần tử CustomPainterExample.buildShape()trả về một ClipPath bao gồm clipper CustomShapeClass và phần tử còn lại CustomPainterExample.buildButtonAnimation() bao gồm một Container với CustomPaint là phần tử con của nó hiển thị ở giữa màn hình. . Phần trăm tăng được hiển thị bằng phương thức onPressed tại nút Raised.

    import 'package:flutter/material.dart';
    class CustomShapeClass extends CustomClipper<Path> {
      @override
      getClip(Size size) {
        // TODO: implement getClip
        var path = new Path();
        path.lineTo(0, size.height / 4.25);
        var firstControlPoint = new Offset(size.width / 4, size.height / 3);
        var firstEndPoint = new Offset(size.width / 2, size.height / 3 - 60);
        var secondControlPoint =
        new Offset(size.width - (size.width / 4), size.height / 4 - 65);
        var secondEndPoint = new Offset(size.width, size.height / 3 - 40);
        path.quadraticBezierTo(firstControlPoint.dx, firstControlPoint.dy,
            firstEndPoint.dx, firstEndPoint.dy);
        path.quadraticBezierTo(secondControlPoint.dx, secondControlPoint.dy,
            secondEndPoint.dx, secondEndPoint.dy);
        path.lineTo(size.width, size.height / 3);
        path.lineTo(size.width, 0);
        path.close();
        return path;
      }
      @override
      bool shouldReclip(CustomClipper oldClipper)
      {
        return true;
      }
    }

    CustomShapeClass kế thừa CustomClipper được minh họa ở trên Trong điều khiển này, các điểm điều khiển và điểm kết thúc được xác định trong đó đường cong bezier bậc hai được vẽ. Bây giờ hãy khai báo lõi của ứng dụng lớp ButtonPainter

    import 'dart:math';
    import 'package:flutter/cupertino.dart';
    import 'package:flutter/material.dart';
    class ButtonPainter extends CustomPainter
    {
      Color buttonBorderColor;
      Color progressColor;
      double percentage;
      double width;
      ButtonPainter({this.buttonBorderColor,this.progressColor,this.percentage,this.width});
      @override
      void paint(Canvas canvas, Size size) {
        Paint line = Paint()
          ..color = buttonBorderColor
          ..strokeCap = StrokeCap.round
          ..style = PaintingStyle.stroke
          ..strokeWidth = width;
        Paint complete =  Paint()
          ..color = progressColor
          ..strokeCap = StrokeCap.round
          ..style = PaintingStyle.stroke
          ..strokeWidth = width;
        Offset center  = Offset(size.width/2, size.height/2);
        double radius  = min(size.width/2,size.height/2);
        canvas.drawCircle(
            center,
            radius,
            line
        );
        double arcAngle = 2*pi* (percentage/100);
        canvas.drawArc(
            Rect.fromCircle(center: center,radius: radius),
            -pi/2,
            arcAngle,
            false,
            complete
        );
      }
      @override
      bool shouldRepaint(CustomPainter oldDelegate) {
        return true;
      }
    }

    Ở đây chúng ta nhận được các thuộc tính khác nhau lineColor, có nghĩa là màu cho đường đi, trên đó tiến trình sẽ được vẽ, completeColor cho tiến độ đã hoàn thành, completePercent cho mức tiến độ đã hoàn thành. Từ tiện ích con của nó, chúng tôi gửi thuộc tính này xuống, ở đây chúng tôi chỉ đơn giản là vẽ.

    ButtonPainter kế thừa lớp CustomPainter và chúng ta cần ghi đè paint phương thức của nó để vẽ đồ họa. Để vẽ, ngay từ đầu, chúng ta cần thông báo một thứ gọi là Paint , nó định hình tất cả các thuộc tính dự kiến ​​sẽ vẽ thứ gì đó trên màn hình. Vì vậy, chúng ta có hai đối tượng Paint ; đầu tiên là một dòng nói lên đường theo dõi mà tiến trình sẽ được vẽ trên đó. Chúng tôi cung cấp cho nó một màu sắc, được cung cấp dưới dạng thuộc tính, strokeCap ​​là hình tròn và chúng tôi yêu cầu style là PaintingStyle cho một trong những cung cấp. Vì vậy, ngoài ra, chúng tôi thông báo đối tượng Paint hoàn chỉnh cho tiến trình đã hoàn thành.

    Tóm lại, chúng ta phải vẽ một vòng tròn đầy đủ và sau đó vẽ một vòng cung trên đó, thế là đã hoàn thành.

    Offset center  = Offset(size.width/2, size.height/2);
    double radius  = min(size.width/2,size.height/2);

    Trong hai đường thẳng này, chúng tôi tính toán các hướng cho tâm của hình tròn và bán kính cơ bản của nó, tâm là chiều rộng và chiều cao của thùng chứa chia cho 2. Chúng tôi cần chính xác tâm của các hình tròn ở giữa. size là những gì chúng ta nhận được dưới dạng một tham số trong hàm paint, là kích thước của thùng chứa. radius Đây là mức tối thiểu của một nửa chiều rộng và chiều cao và bây giờ chúng tôi đang lấy mức tối thiểu của cả hai vì vùng chứa có thể không phải luôn luôn là hình vuông

    canvas.drawCircle(center,radius,line);

    Bây giờ chúng ta chỉ vẽ với hàm drawCircle được gọi trên đối tượng canvas được cung cấp, chúng ta chuyền vào tâm, bán kính và đối tượng Paint .

    canvas.drawArc(Rect.fromCircle(center: center,radius: radius),
        -pi/2,arcAngle,false,complete);

    Bây giờ, chúng ta tìm góc cho cung tròn đã hoàn thành, mà nó đã xuất hiện. Tôi không đi sâu vào tính toán hình học. Để gọi drawArc, chúng ta cần chỉ định một Rect , là hộp giới hạn của cung tròn, chúng ta nhận được Rect bao gồm các tham số của đường tròn mà chúng ta đã vẽ trước đó. Tại thời điểm đó, chúng ta cho một góc bắt đầu là -pi / 2 radian, hãy nhớ nó không phải là 0. Đỉnh là -pi/2, 0 là đích ngoài cùng bên phải của đường tròn. Chúng tôi cung cấp arcAngle tại điểm đó, đó là cung sẽ kéo dài bao nhiêu. Sau đó, chúng tôi chuyền vào false để nói rằng chúng tôi không muốn phần cuối của vòng cung được kết nối trở lại trung tâm và cuối cùng chúng chuyền vào complete. Đó là tất cả những gì chúng tôi mong đợi để vẽ bằng cách sử dụng CustomPaint.

    Bây giờ, hãy thêm các hình ảnh động.

    percentageAnimationController = new AnimationController(vsync: this,
        duration: new Duration(milliseconds: 1000))
      ..addListener((){setState(() {
          percentage=lerpDouble(percentage,newPercentage,
          percentageAnimationController.value);});});

    Chúng tôi thêm hoạt ảnh trên màn hình HomePage trong initState(). Sau đó, chúng tôi đã thêm một trình nghe trên AnimationController đó được gọi ở mỗi bước của hoạt ảnh và chúng tôi thay đổi tỷ lệ phần trăm. Để thay đổi tỷ lệ phần trăm, chúng tôi nhận được một giá trị được chèn với sự trợ giúp của hàm lerpDouble , giá trị nằm trong percentage và newPercentage lớn hơn giá trị ban đầu 10, chúng ta nhận dựa trên giá trị AnimationController’s.

    Chúng ta thêm dòng này vào nút onPressed để bắt đầu hoạt ảnh.

    percentageAnimationController.forward(from: 0.0);

    Bạn sẽ thấy mã đầy đủ trên GitHub và đây là một ví dụ về quy trình vòng tròn nhỏ để tích hợp với custom paint và video dưới đây cho biết cách CustomPaint sẽ hoạt động.

    Hy vọng blog này sẽ cung cấp cho bạn đầy đủ thông tin trong việc thử Custom Paint trong các dự án hiện tại của bạn Cảm ơn vì đã đọc bài viết này

  • Cách sử dụng các biến môi trường trong Docker Compose

    Sớm hay muộn, tất cả chúng ta đều phải xử lý các biến môi trường trong tệp soạn của mình. Chúng có thể trở thành một nỗi đau, đặc biệt nếu chúng ta không biết cách sử dụng chúng đúng cách. Dưới đây là mọi thứ tôi biết về các biến môi trường và cách sử dụng các biến này dễ dàng và trên hết là an toàn.

    Chúng ta sử dụng các biến môi trường như thế nào?

    Docker Compose cho phép chúng ta chuyển các biến môi trường vào thông qua dòng lệnh hoặc xác định chúng trong trình bao của chúng ta. Tuy nhiên, tốt nhất là giữ các giá trị này bên trong tệp soạn thực tế và bên ngoài dòng lệnh. “Tại sao?” bạn có thể yêu cầu.

    Bởi vì theo cách này, chúng ta không phải nhớ tất cả các biến môi trường mà chúng ta sử dụng mỗi khi triển khai vùng chứa của mình. Bằng cách lưu trữ chúng trong tệp soạn, chúng ta duy trì tính nhất quán trong quá trình xây dựng của mình.

    Có nhiều hướng khác nhau để làm điều đó.

    Sử dụng lựa chọn môi trường.

    Sử dụng tùy chọn môi trường soạn cho phép chúng ta khai báo các biến môi trường và giá trị của chúng bên trong tệp soạn của chúng ta, như thế này.

    Đây là cách dễ nhất, nhanh nhất để lưu trữ các biến môi trường bên trong soạn tệp. Tuy nhiên, nó có một nhược điểm lớn là liên quan đến bảo mật. Bạn có đoán được nó là gì không?

    Đúng rồi.

    Việc lưu trữ các giá trị của các biến môi trường của bạn trong tệp Soạn sẽ – 95% thời gian – chuyển thẳng đến kiểm soát nguồn và đó là một rủi ro bảo mật rất lớn. May mắn thay, chúng ta có một giải pháp thay thế: sử dụng tệp bên ngoài để lưu trữ các biến môi trường của chúng ta.

    Sử dụng tập tin .env

    Ưu điểm chính của việc sử dụng tệp bên ngoài cho các biến môi trường của bạn là bạn có thể giữ tệp đã nói ngoài quyền kiểm soát nguồn của mình. Rốt cuộc, không ai thích có mật khẩu, khóa API hoặc thông tin siêu bí mật khác của họ trên internet.

    Tệp .env là tệp văn bản thuần túy, chúng ta sử dụng để cấu hình. Hãy nhớ rằng, vì tên tệp bắt đầu bằng dấu ‘ . ’ Nên chúng vẫn bị ẩn đối với hệ thống.

    Chú ý: Để liệt kê các tệp ẩn, bạn có thể sử dụng lệnh ls -a trên Linux hoặc lệnh dir / a: h trên Windows.

    Bạn phải tạo tệp .env tại thư mục gốc của dự án, đây cũng là nơi chứa tệp docker-compost.yml của bạn.

    Chúng ta có thể khai báo và gán các biến trong tệp .env của mình. Đặt tên cho các biến theo cách bạn muốn vì chúng ta sẽ chỉ truy cập các giá trị của chúng.

     Đây là tệp .env của tôi:

    Bạn cũng có thể tạo và điền tệp .env của mình từ dòng lệnh bằng cách sử dụng lệnh mèo Linux:

    Chú ý: Hãy nhớ không để lại bất kỳ khoảng trống nào giữa dấu = và giá trị được gán cho biến của bạn, vì chúng sẽ được thêm vào chuỗi.

    Bây giờ chúng ta đã lưu trữ các biến trong tệp .env, hãy sử dụng chúng trong tệp Soạn của chúng ta. Bây giờ là lúc sử dụng phép nội suy chuỗi (đó là một cái tên ưa thích khi sử dụng ký hiệu $ {string}) để gán giá trị của các biến .env của chúng ta cho các biến môi trường trong tệp Soạn.

    Như bạn có thể thấy, chúng ta duy trì tùy chọn môi trường và chỉ cần gán các giá trị bên ngoài của chúng ta cho các biến môi trường Soạn.

    Để kiểm tra xem mọi thứ có hoạt động bình thường hay không, hãy chạy lệnh sau: docker-compile up.

    Chú ý: Bạn có thể kiểm tra giá trị nào được gán cho các biến môi trường bằng cách chạy lệnh sau (trong một thiết bị đầu cuối khác): docker-compile config.

    Mức độ ưu tiên biến môi trường

    Một điều rất quan trọng mà chúng ta phải ghi nhớ là mức độ ưu tiên được Compose sử dụng để lựa chọn giá trị môi trường của nó. Điều đó có nghĩa là gì?

    Nếu chúng ta khai báo cùng một biến môi trường trong một số tệp (ví dụ: trong tệp Soạn và trong tệp .env bên ngoài) với các giá trị khác nhau, thì Soạn sẽ sử dụng giá trị của biến được khai báo trong tệp Soạn. Tại sao? Bởi vì, tùy thuộc vào nơi mà biến được khai báo, Soạn sẽ cấp cho biến đó mức độ ưu tiên cao hơn hoặc thấp hơn. Đây là thứ tự, xếp hạng từ mức độ ưu tiên cao nhất đến mức độ ưu tiên thấp nhất:

    1. Soạn tệp
    2. Biến môi trường Shell
    3. Tệp môi trường
    4. Dockerfile
    5. Biến không được xác định

    Nếu vì lý do nào đó, tính năng Soạn đang chọn và chỉ định một giá trị mà bạn không mong đợi, thì đây có thể là nguyên nhân. Đảm bảo rằng tất cả các biến của bạn được khai báo chính xác ở nơi bạn muốn.

    Sử dụng tùy chọn env file

    Trong phần trước, chúng ta đã nói về các tệp .env thuần túy, nhưng chúng ta chưa bao giờ sử dụng các tệp .env có tên. Nếu chúng ta muốn tệp .env của mình có tên, như secret-things.enf, thì Compose có một tùy chọn nhỏ tiện lợi có tên là env_file.

    Tùy chọn này cho phép chúng ta thông báo cho soạn thảo tệp .env mà nó phải tìm kiếm, trái ngược với hành vi mặc định của nó, đó là tìm kiếm tệp .env không tên. Đây là cách chúng ta sử dụng tùy chọn env_file.

    Như bạn có thể thấy, chúng ta đã thêm tùy chọn env_file, tùy chọn này trỏ đến một tệp có tên tệp secret -uff.env. Tất cả những gì còn lại là đổi tên tệp .env trước đó của chúng ta thành secret -uff.env.

    Bạn có thể nhận thấy tùy chọn môi trường không còn xuất hiện trong tệp soạn thư của chúng ta. Điều này là do việc sử dụng tùy chọn env_file làm nảy sinh một vấn đề – một vấn đề khiến tôi khá đau đầu!

    Để gán các giá trị được khai báo trong tệp .env có tên bên ngoài cho các biến soạn yêu cầu tệp đã nói phải được xác định trong dịch vụ soạn chính. Điều này liên quan đến thứ tự mà Soạn tuân theo khi thực hiện các hoạt động. Tuy nhiên, chúng ta không có dịch vụ chính, vì chúng ta chỉ sử dụng một dịch vụ db. Do đó, nếu chúng ta cố gắng triển khai tệp Soạn của mình, nó sẽ phàn nàn rằng các biến của chúng ta không được xác định và thay thế chúng bằng các chuỗi trống.

    Bạn không cần phải nghe lời tôi, hãy tiếp tục và thử nó! Chạy docker-soạn lên và xem điều gì sẽ xảy ra.

    Vì vậy, làm thế nào để chúng ta giải quyết vấn đề này? Đây là những gì tôi đã tìm ra:

    Nếu chúng ta xóa tùy chọn môi trường khỏi tệp soạn, khi triển khai, soạn sẽ tìm kiếm tệp secret -uff.env được chỉ định, điều mà tùy chọn này không thực hiện khi có tùy chọn môi trường. Vấn đề đã được giải quyết!

    Tuy nhiên, hãy nhớ rằng vì chúng ta không còn tùy chọn môi trường nữa, chúng ta phải khai báo các biến môi trường trực tiếp trong tệp secret -uff.env, như sau:

    Một lần nữa, để kiểm tra xem mọi thứ có hoạt động bình thường hay không, hãy chạy: docker-compile up.

    Đây các cách khác nhau để xử lý an toàn với các biến môi trường trong soạn tệp. Cảm ơn đã theo dõi.

    Author: DuongVT19

  • Flutter – Các phím tắt IDE để giúp bạn code nhanh hơn

    Flutter – Các phím tắt IDE để giúp bạn code nhanh hơn

    Flutter – Các phím tắt IDE để giúp bạn code nhanh hơn

    Nếu bạn chưa quen với việc phát triển Flutter thì bạn phải tìm hiểu kỹ về các cấu trúc lồng vào nhau (nested structures) để biết mức độ khó khăn để thêm hoặc xóa các widget ở giữa code hay để tìm một nơi mà một widget kết thúc và một widget khác bắt đầu. Sau đó, bạn phải dành cả ngày để khớp các dấu ngoặc mở với dấu đóng của chúng. Chúng mình đã mất thời gian để tìm ra các phím tắt. Vì vậy, bạn sẽ không phải mất thời gian để thực hiện lại điều đó vì mình sẽ cung cấp cho bạn; và mình đã sắp xếp tất cả các phím tắt cho phép phát triển nhanh hơn và mượt mà hơn trong Flutter.

    Note: Tất cả các phím tắt này đều hoạt động cho Android Studio và IntelliJ trong Windows.

    Tạo Stateless widget hoặc Stateful widget

    Đoán xem nào? Bạn không phải viết thủ công các widget class của mình và ghi đè các build function. IDE có thể làm điều đó thay bạn.

    Bạn chỉ cần gõ stless để tạo một Stateless Widget như sau:

    Hoặc stful để tạo Stateful widget:

    Điều gì sẽ xảy ra nếu bạn đã tạo một StatelessWidget và thêm nhiều children, nhưng sau đó nhận ra rằng sau cùng thì bạn sẽ cần một State? Bạn có nên tạo một StatefulWidget mới và sau đó chuyển tất cả code của bạn sang nó theo cách thủ công? Bạn không cần phải làm như vậy.

    Bạn chỉ cần đặt con trỏ vào StatelessWidget, nhấn Alt + Enter và nhấp vào Convert to StatefulWidget. Tất cả code soạn sẵn sẽ được tạo tự động cho bạn.

    Những điều kỳ diệu hơn bạn có thể làm với Alt + Enter

    Alt + Enter là cây đũa thần giúp bạn sử dụng để phát triển nhanh hơn trong Flutter. Bạn có thể nhấp vào bất kỳ tiện ích con nào, nhấn Alt + Enter để xem bạn có những tùy chọn nào cho tiện ích cụ thể đó.

    Thêm Padding xung quanh Widget

    Giả sử bạn có một widget không phải là Container, vì vậy nó không có padding property. Bạn muốn cung cấp một số padding nhưng lại sợ làm rối widget structure của mình. Với cây đũa thần của chúng mình, bạn có thể thêm Padding của mình mà không làm rối tung bất cứ thứ gì:

    Chỉ cần nhấn Alt + Enter trên widget cần một padding xung quanh nó và nhấp vào Add Padding. Bây giờ bạn có thể sửa đổi padding mặc định thành bất kỳ thứ gì bạn muốn.

    Center Widget

    Đây không phải là điều gì quá phi thường. Nó chỉ căn giữa widget của bạn trong không gian có sẵn. Điều này không hoạt động bên trong Column hoặc Row.

    Wrap bằng Container, Column, Row hoặc bất kỳ Widget nào khác

    Bạn có thể sử dụng cách tiếp cận tương tự để bọc widget của mình bằng Container. Vì vậy, Container mới sẽ trở thành parent của Widget của bạn.

    Hoặc, bạn thậm chí có thể kết hợp nhiều widget với một Colunm hoặc Row chỉ trong một cú nhấp chuột!

    Hoặc bọc chúng bằng bất kỳ widget nào khác:

    Bạn thậm chí có thể bọc chúng bằng StreamBuilder nếu bạn có phiên bản plugin Flutter mới nhất. Cảm ơn Bhavik Makwana đã cho mình biết về điều đó.

    Bạn không thích một widget nào đó? Hãy loại bỏ nó bằng Magic Wand.

    Đúng vậy, việc xóa một widget cũng dễ dàng như thêm một widget mới.

    Xem code bên trong Widget

    Đó là điều tốt nhất về một open source framework. Nếu bạn muốn biết điều gì đang diễn ra đằng sau một widget tuyệt vời hoặc một class, thì bạn có thể đặt con trỏ vào nó và nhấn Ctrl + B. Nó sẽ hoạt động như một liên kết, đưa bạn đến thẳng source code của Widget, nơi bạn có thể đọc mọi thứ về nó. Flutter cũng sử dụng các bình luận để giải thích rất nhiều code, một tài liệu tuyệt vời.

    Kiểm tra các thuộc tính của Widget  mà không cần rời khỏi tệp hoặc tab

    Nếu bạn muốn kiểm tra xem Widget của bạn có thể làm được những điều tuyệt vời nào mà không cần rời khỏi tệp của bạn để tìm hiểu tài liệu, chỉ cần nhấn Ctrl + Shift + I để xem nhanh constructor của Widget.

    Chọn nhanh toàn bộ Widget

    Rất nhiều lần chúng ta cần giải nén/xóa toàn bộ widget và chúng ta cố gắng chọn chúng theo cách thủ công:

    Nếu đó là một widget thực sự lớn, thì việc tìm ra dấu đóng ngoặc thuộc về Widget nào có thể rất mất thời gian và chúng ta không muốn làm rối toàn bộ cấu trúc của mình.

    Những lúc như thế này, mình thích sử dụng phím tắt siêu hữu ích này.

    Chỉ cần nhấp vào widget bạn muốn giải nén và nhấn Ctrl + W. Toàn bộ Widget sẽ được chọn cho bạn mà không cần di chuyển con trỏ của bạn một inch.

    Format code

    Đôi khi code của bạn sẽ chỉ là một mớ hỗn độn. Kiểu như thế này:

    Bây giờ, hầu hết các IDE đều có tính năng này, (mặc dù có thể không phải là tổ hợp phím giống nhau). Chỉ cần nhấn Ctrl + Alt + L để sửa lỗi thụt lề và định dạng lại code của bạn.‌

    Xem outline UI ứng dụng của bạn

    Hầu hết các Widget của chúng ta không chỉ có một child trong tree của chúng. Chúng có những children tree có children riêng của chúng và nhiều thứ khác nữa. Nếu Widget của bạn có các children được lồng vào nhau phức tạp, thì bạn có thể gặp chút rắc rối với việc hiểu cấu trúc của code. Nhưng rất may là chúng ta có Flutter Outline để giải cứu!

    Bạn có thể tìm thấy Flutter Outline ở  bên phải IDE của mình. Nó là một trong những tab dọc và nằm ngay phía trên Flutter Inspector. Khi bạn mở nó lên, nó trông giống như sau:

    Giờ đây, bạn có thể thấy rõ Widget nào đang ở đâu, chúng được sắp xếp như thế nào trong giao diện người dùng và widget nào có các children widget khác. Dễ như ăn bánh!

    Extract code thành một method

    Flutter Outline là một công cụ khá hữu ích. Bạn có thể thực hiện hầu hết những việc bạn đã làm với Alt + Enter, chẳng hạn như bọc một Column và Center một Widget, nhưng có những thứ tuyệt vời hơn nữa có sẵn trong tab Flutter Outline! Một trong số đó là nút Extract Method.

    Nếu bạn cảm thấy như đang viết một Widget quá dài và có lẽ phải là một Widget tùy chỉnh, thì thay vì chuyển code theo cách thủ công thành một method, bạn có thể sử dụng công cụ này để làm điều kỳ diệu cho mình!

    Move Widget lên và xuống

    Một điều điên rồ khác mà bạn có thể làm với Flutter Outline là nếu bạn có nhiều children trong một widget, bạn có thể dễ dàng sắp xếp lại thứ tự của chúng:

    Bạn cũng có thể di chuyển chỉ một dòng lên hoặc xuống bằng cách nhấn Shift + Alt + Up / Down

    Refactor Renaming

    Đây là một công cụ khá cơ bản mà hầu hết các IDE đều có. Điều này cho phép bạn đổi tên một method, Widget, class hoặc tên tệp và nó đảm bảo rằng các tham chiếu đến nó cũng được đổi tên. Chỉ cần sử dụng Shift + F6 và nhập tên mới:

    Remove Unused Imports

    Vì vậy, bạn đang làm việc trên một dự án và bạn đã import rất nhiều file, nhưng theo thời gian, code của bạn ngày càng được tối ưu hóa. Cuối cùng, bạn có thể không cần những file đó nữa. Có thể bạn thường xóa chúng theo cách thủ công, nhưng mình đã ở đây để giúp bạn thực hiện nó dễ dàng hơn, đây là một tổ hợp bàn phím khá đẹp: Ctrl + Alt + O

    Đó là tất cả các phím tắt mà mình biết bây giờ. Hãy nhớ kiểm tra lại thường xuyên để biết thêm các mẹo, thủ thuật và những thứ tuyệt vời khác!

    Bài viết được lược dịch từ Pooja Bhaumik.

  • Cloud Architect

    Ngày càng có nhiều công ty nghe thấy tiếng còi báo động của đám mây. Với chi phí khởi động thấp và dễ dàng thiết lập cơ sở hạ tầng, tại sao lại không?

    Nhưng khả năng loại bỏ phần cứng tại chỗ để chuyển sang sử dụng đám mây – nơi người khác phải tham gia vào việc bảo trì và cập nhật máy chủ – vẫn đòi hỏi các chuyên gia có thể làm cho mọi thứ hoạt động trơn tru.

    Chính xác thì kiến trúc sư đám mây là gì?

    “Khi bạn nghĩ về một kiến ​​trúc sư, bạn sẽ nghĩ đến một người nào đó thiết kế các tòa nhà và lên kế hoạch xây dựng chúng như thế nào; Meg Yahl, kỹ sư đám mây tại Uncomn, một công ty dịch vụ tư vấn công nghệ và quản lý doanh nghiệp cho biết.

    Cụ thể hơn, một kiến ​​trúc sư đám mây giúp một công ty phát triển, thực hiện và duy trì chiến lược điện toán đám mây của mình. Họ cần có hiểu biết sâu sắc về nhu cầu, ràng buộc, quy định ngành và mục tiêu của công ty họ vì nó liên quan đến điện toán đám mây.

    Susanne Tedrick, chuyên gia cơ sở hạ tầng đám mây tại Nền tảng điện toán đám mây Azure của Microsoft và trước đây là chuyên viên kỹ thuật tại IBM Cloud. Mặc dù bản thân không phải là kiến ​​trúc sư đám mây, Tedrick hợp tác chặt chẽ với các kiến ​​trúc sư đám mây trong quá trình giúp khách hàng sử dụng và di chuyển đám mây.

    Cô nói thêm rằng vai trò của kiến trúc sư đám mây đòi hỏi kiến thức kỹ thuật rất sâu về điện toán đám mây và các lĩnh vực bổ trợ như mạng máy tính, dữ liệu và bảo mật.

    Một số tiêu đề thường trùng lặp hoặc được coi là có thể hoán đổi cho nhau với các kiến trúc sư đám mây. Chúng bao gồm các kỹ sư đám mây và kiến trúc sư giải pháp trong các công ty hoặc phòng ban cụ thể về đám mây. Ví dụ: mặc dù chức danh của Yahl là kỹ sư đám mây – với các kỹ sư đám mây có xu hướng là những người thực hiện các thiết kế và kế hoạch do kiến trúc sư đám mây phát triển – cô ấy thường xuyên thực hiện cả các công việc dành riêng cho kỹ sư và kiến trúc sư.

    Tedrick ghi nhận phần lớn các biến thể tiêu đề và sự trùng lặp trong vai trò kiến trúc sư đám mây đối với sự phát triển nhanh chóng của nó khi việc áp dụng đám mây ngày càng phát triển.

    Bà nói: “Đặc biệt khi có những chuyên ngành phụ trong lĩnh vực này, các công ty sẽ thường kết hợp vai trò kiến trúc sư với trách nhiệm kỹ sư hoặc kỹ thuật viên.

    Không chỉ tương tác với đám mây

    Nói chung, trải nghiệm hàng ngày của kiến ​​trúc sư đám mây sẽ xoay quanh việc xác định, đáp ứng và thực hiện nhu cầu đám mây của công ty hoặc khách hàng, Tedrick nói. Điều này có thể có nghĩa là dẫn đầu các cuộc thảo luận chuyên sâu về kỹ thuật, xác nhận rằng giải pháp đám mây sẽ đáp ứng các yêu cầu của công ty hoặc xác định các cách giải quyết tiềm năng, phát triển sơ đồ kiến ​​trúc và giúp phát triển bằng chứng về công nghệ và bằng chứng về khái niệm cho khách hàng. Đôi khi, nó cũng có thể liên quan đến việc cung cấp sản phẩm chung và giáo dục điện toán đám mây.

    Đối với Frank Dagenhardt – kiến ​​trúc sư giải pháp cho Zscaler, một công ty bảo mật thông tin dựa trên đám mây – giáo dục những người khác về đám mây là một tính năng thường xuyên trong những ngày của ông. Điều này có thể liên quan đến việc viết sách trắng và bài đăng trên blog, hoặc tổ chức hội thảo trên web của nhóm người dùng trong ngành. Và các nỗ lực giảng dạy có thể hướng đến đối tượng nội bộ như nhân viên của Zscaler hoặc đối tác hoặc khách hàng bên ngoài.

    “Chúng tôi làm việc dựa trên sự hỗ trợ cho các nhóm nội bộ của chúng tôi và các đối tác của chúng tôi mà chúng tôi làm việc cùng – đảm bảo rằng họ hiểu các giải pháp, biết cách chúng hoạt động, những loại giải pháp khác nhau mà chúng tôi có thể giúp khách hàng và cách họ sẽ giúp khách hàng, ” Anh nói.

    Gặp gỡ khách hàng cũng là một phần chính trong các ngày của Dagenhardt. Anh và nhóm của mình thảo luận với khách hàng về nhu cầu của họ và cách đạt được những nhu cầu đó một cách tốt nhất. Các cuộc họp này thường tạo ra các ý tưởng để cải tiến các sản phẩm và dịch vụ của Zscaler, thông qua yêu cầu của khách hàng hoặc nhóm xác định các tính năng chung có thể phục vụ khách hàng tốt hơn.

    Dagenhardt cho biết: “Nếu có những khoảng trống về tính năng mà chúng tôi có thể mắc phải hoặc những thứ mà khách hàng đang tìm kiếm mà chúng tôi không giải quyết ngay hôm nay, thì chúng tôi có thể tiếp tục và đưa ra những đề xuất đó”.

    Việc chuyển đổi giữa giao tiếp với khách hàng và các nhóm kinh doanh kỹ thuật nội bộ đòi hỏi các kiến ​​trúc sư đám mây phải có kỹ năng giao tiếp mạnh mẽ. Vì kiến ​​trúc sư đám mây làm việc trên cơ sở hạ tầng kỹ thuật số cho phép một công ty hoạt động, họ có cơ hội tương tác và dẫn dắt các cuộc thảo luận kỹ thuật với nhiều đối tượng khác nhau như nhà phát triển, kỹ sư mạng, nhà quản lý, v.v.

    Đối với Yahl, công việc cơ sở hạ tầng là thứ chi phối cuộc sống của cô gần đây. Tại Uncomn, cô ấy đã tìm hiểu khá nhiều về cơ sở hạ tầng mạng và cách triển khai nó. Nhưng rất nhiều việc cô ấy đang làm gần đây đặc biệt liên quan đến rất nhiều cơ sở hạ tầng dưới dạng mã. Điều này giúp tăng tính nhất quán trong toàn công ty và giảm cơ hội mắc lỗi của con người.

    “Bạn muốn đảm bảo rằng nếu bạn cần tạo ra một ngăn xếp khác, một tập hợp máy chủ khác và tất cả những thứ đi kèm với nó, bạn muốn đảm bảo rằng bạn luôn làm theo cùng một cách,” cô nói.

    Học tập là một phần của thói quen hàng ngày

    Đó là kinh nghiệm phổ biến đối với các kiến ​​trúc sư đám mây.

    Yahl nói: “Cánh đồng khác nhau mỗi ngày. “Đó là một trong những điều mà nếu bạn không phấn đấu học hỏi, thì bạn sẽ bị tụt lại phía sau”.

    Dagenhardt cho biết tốc độ phát triển nhanh chóng của công nghệ đám mây là một trong những điều khiến anh ấy thức đêm. Ông chỉ ra rằng AWS đã mở rộng cung cấp của mình lên hơn 200 dịch vụ vào tháng 9. Và đó chỉ là một nhà cung cấp.

    Luôn cập nhật các tùy chọn, cách khách hàng có thể tận dụng các công nghệ mới và các phương pháp hay nhất có thể giúp bảo mật chúng, tất cả đều yêu cầu học hỏi liên tục. Đối với Dagenhardt, việc duy trì các chứng chỉ và học hỏi những điều mới khiến anh ấy cảm thấy mình luôn đứng đầu cuộc chơi.

    “Đó luôn là điều đã giúp ích cho tôi – dành một chút thời gian của tôi cho sự thăng tiến và cải thiện cá nhân,” anh nói.

    Yahl cũng nói về tầm quan trọng của việc tiếp tục học tập và các chứng chỉ của bạn, nhưng cô ấy cũng nhấn mạnh giá trị của việc học hỏi từ những người bạn làm việc cùng.

    “Các bạn học hỏi lẫn nhau, đặc biệt là các phím tắt hoặc công cụ giúp cuộc sống dễ dàng hơn,” cô nói, đồng thời đưa Visual Studio Code và ShellCheck làm ví dụ về các nguồn mà cô học được từ đồng nghiệp.

    Nhưng cô ấy nói rằng không chỉ đơn thuần là chọn những công cụ hữu ích có thể giúp một ngày trở nên dễ dàng hơn. Ngoài ra còn có yếu tố hợp tác là có được những quan điểm khác nhau về cách sử dụng hoặc triển khai các công nghệ khác nhau.

    “Khi bạn đang làm việc một mình, bạn đang ở trong khoảng trống và bạn không có ai để xem mã của mình, không ai để đảm bảo rằng bạn đang tuân thủ các phương pháp hay nhất tốt nhất có thể hoặc biết về những cách làm mới và đang phát triển Yahl nói.

    Trong những tình huống mà một kiến ​​trúc sư đám mây có thể không có đồng nghiệp để học hỏi từ đó, Yahl đề xuất những địa điểm như Reddit, nơi những người khác trong cộng đồng công nghệ đến để đặt câu hỏi cho nhau về các tài nguyên tốt nhất cho các tình huống khác nhau.

    “Về cơ bản, bạn có một danh sách những thứ mà mọi người sẽ giới thiệu và sau đó bạn nhận được quan điểm của người khác về những điều tốt và xấu về những công cụ đó và có một cuộc trò chuyện diễn ra liên tục,” cô nói. “Vì vậy, ngay cả khi bạn không có đồng nghiệp hướng dẫn, bạn vẫn có thể nhìn thấy nhiều quan điểm đó.”

    Mối quan tâm về bảo mật trong lĩnh vực đa đám mây, phát triển nhanh

    Hiểu và có thể làm việc trên nhiều nền tảng là nhu cầu ngày càng tăng đối với các kiến ​​trúc sư đám mây. Dagenhardt khuyên những người sẽ là kiến ​​trúc sư đám mây nên có sự kết hợp giữa các quan điểm khi nói đến nền tảng đám mây vì đó là nơi anh ấy nhìn thấy tương lai của lĩnh vực này.

    “Những gì chúng tôi đã thấy trong ngành công nghiệp đám mây là, trong khi hầu hết khách hàng bắt đầu từ một nhà cung cấp đám mây duy nhất, theo thời gian, họ trở thành một môi trường đa đám mây, nơi họ lưu trữ một số tài nguyên của mình trong một hoặc nhiều đám mây,” ông nói. Ông cũng nói rằng ông đang thấy sự thay đổi ngày càng tăng từ cơ sở hạ tầng như một dịch vụ sang các thùng chứa hoặc thậm chí là các công nghệ không máy chủ.

    Theo Tedrick, xu hướng đa dạng hóa đám mây này, các chiến lược và các yếu tố dựa trên đám mây khác như “cuộc chiến điện toán đám mây lớn hơn giữa AWS, Google và Microsoft” sẽ khiến vai trò kiến ​​trúc sư đám mây trở nên thịnh hành. Điều này có nghĩa là các nhà tuyển dụng đang tìm kiếm kiến ​​trúc sư đám mây sẽ muốn biết các kiến ​​trúc sư có kinh nghiệm thực hành về nền tảng đám mây nào.

    Bảo mật trong môi trường ngày càng mở rộng và không có kỹ thuật này đang và sẽ tiếp tục là mối quan tâm của những người làm việc với đám mây.

    Yahl nói: “Chúng tôi sẽ có những tính toán trong thế giới an ninh, đó là điều chắc chắn. “Có rất nhiều người muốn tham gia vào lĩnh vực này và họ học những kiến ​​thức cơ bản của họ, nhưng họ không học được sự bảo mật đằng sau nó.”

    Điều này một phần là do tốc độ của trường đám mây vượt xa khả năng của hầu hết các nền giáo dục chính quy trong việc cập nhật đào tạo về bảo mật, nhưng cũng bởi vì đám mây, theo Yahl, “rào cản gia nhập thấp nhất đối với bất kỳ ai cần thiết lập cơ sở hạ tầng của họ. ”

    Bà nói: “Đám mây rất tuyệt vời để quay các máy chủ và cơ sở hạ tầng và tất cả điều đó thực sự, rất nhanh chóng và thực sự dễ dàng,” cô nói, đồng thời cho biết thêm rằng nhiều giá trị mặc định trong môi trường đám mây có thể không an toàn. “Đám mây là một cách để thiết lập và chạy rất nhanh nhưng bạn cũng phải bảo mật nó.”

    Tốc độ tiến bộ của công nghệ đám mây cũng khiến Dagenhardt đặt ra các câu hỏi bảo mật về tương lai.

    Ông nói: “Chúng tôi vẫn có công nghệ cũ hơn và kế thừa hơn mà chúng tôi phải có khả năng xử lý và thích ứng. “Làm cách nào để chúng ta bảo vệ môi trường truyền thống cũng như bảo vệ môi trường đám mây và đảm bảo rằng nó có thể quản lý được và không tạo ra gấp đôi công việc cho khách hàng?”

    Làm thế nào để trở thành một kiến trúc sư đám mây

    Nếu không ngừng học hỏi và thích ứng với nhu cầu thay đổi của khách hàng và cảnh quan đám mây đang phát triển nghe có vẻ hấp dẫn, thì có một số lời khuyên cần ghi nhớ. Tedrick cho biết: Đứng đầu trong số đó là các kiến ​​trúc sư đám mây phải biết và hiểu rõ về điện toán đám mây ngoài những điều cơ bản.

    “Họ sẽ cần hiểu các phương pháp hay nhất về bảo mật đám mây doanh nghiệp, quản trị, tối ưu hóa chi phí, kiến ​​trúc đám mây kết hợp – sử dụng môi trường đám mây và tại chỗ – và kiến ​​trúc đa đám mây – sử dụng hai hoặc nhiều đám mây”, cô nói. “Trải nghiệm thực tế trên nền tảng đám mây nơi họ đang tạo và giám sát tài nguyên là điều bắt buộc, ưu tiên một năm kinh nghiệm trở lên.”

    Đối với những người không có cơ hội làm việc trên đám mây tại chỗ, Tedrick đã đề xuất các nền tảng bài học trực tuyến như A Cloud Guru và Pluralsight để cung cấp cho người dùng trải nghiệm thực tế với các nền tảng đám mây khác nhau.

    Khi đề cập đến việc ứng tuyển vai trò kiến ​​trúc sư đám mây, Yahl cho biết những người tìm việc không nên nản lòng nếu một bài đăng bao gồm các yêu cầu bạn không có hoặc liên quan đến các nhiệm vụ bạn không quen thuộc. Cô cũng chỉ ra rằng các tin tuyển dụng đôi khi không được viết bởi các chuyên gia kỹ thuật và yêu cầu những điều không thể. Đây có thể là một cái gì đó giống như yêu cầu nhiều năm kinh nghiệm trong một sản phẩm hơn nó đã tồn tại.

    “Nếu bạn đang muốn dấn thân vào lĩnh vực này, đừng để điều gì khiến bạn sợ hãi về vị trí mà bạn có hầu hết các kỹ năng,” cô khuyên.

    Yahl cũng khuyến khích những người muốn tham gia vào lĩnh vực kiến ​​trúc đám mây để thể hiện kỹ năng của họ và thực tế là họ đã phát triển và thay đổi trong những năm qua. Cô ấy chỉ ra Github hoặc các nền tảng khác nơi người dùng có thể lưu trữ mã là những nơi tuyệt vời để tạo danh mục đầu tư mã hóa.

    Chứng chỉ rất quan trọng đối với các kiến ​​trúc sư đám mây tiềm năng. Đây có thể là nhà cung cấp cụ thể và nhà cung cấp bất khả tri. Các chứng nhận tổng quát hơn có thể hữu ích cho thông tin chung và các phương pháp hay nhất về đám mây, nhưng theo Dagenhardt, các chứng chỉ dành riêng cho nhà cung cấp thường hữu ích hơn vì chúng sẽ giải quyết các sản phẩm đám mây cụ thể và các phương pháp hay nhất của chúng.

    Tedrick cũng chỉ ra rằng các chứng chỉ của các nhà cung cấp chính – Microsoft, AWS, Google và IBM – có nhiều khả năng được các nhà tuyển dụng tiềm năng công nhận hơn.

    Nhưng cô ấy nhấn mạnh rằng “mục tiêu phải là đạt được kỹ năng và kinh nghiệm thay vì đạt được chứng chỉ, vì chứng chỉ sẽ chỉ giúp bạn có được cho đến nay, và các kiến ​​trúc sư vĩ đại được sinh ra từ thử thách, sai lầm và thời gian.”

    Cảm ơn mọi người đã theo dõi. Author: DuongVT19.

  • [Flutter] Bố cục Layout (Phần 3)

    Container

    Một trong những Widget được sử dụng nhiều nhất – và vì những lý do này:

    Container as a layout tool

    Khi bạn không chỉ định height và width của Container, nó sẽ khớp với child kích thước của nó

    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(title: Text('Container as a layout')),
        body: Container(
          color: Colors.yellowAccent,
          child: Text("Hi"),
        ),
      );
    }

    Nếu bạn muốn kéo dài Container để khớp với cha của nó, hãy sử dụng double.infinity cho các thuộc tính heightwidth

    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(title: Text('Container as a layout')),
        body: Container(
          height: double.infinity,
          width: double.infinity,
          color: Colors.yellowAccent,
          child: Text("Hi"),
        ),
      );
    }

    Container as decoration

    Bạn có thể sử dụng thuộc tính color để làm thay đổi màu nền của Container bằng decoration và foregroundDecoration.

    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(title: Text('Container.decoration')),
        body: Container(
          height: double.infinity,
          width: double.infinity,
          decoration: BoxDecoration(color: Colors.yellowAccent),
          child: Text("Hi"),
        ),
      );
    }

    . . .

    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(title: Text('Container.foregroundDecoration')),
        body: Container(
          height: double.infinity,
          width: double.infinity,
          decoration: BoxDecoration(color: Colors.yellowAccent),
          foregroundDecoration: BoxDecoration(
            color: Colors.red.withOpacity(0.5),
          ),
          child: Text("Hi"),
        ),
      );
    }

    Container as Transform

    Nếu bạn không muốn sử dụng widget Transform để thay đổi bố cục của mình, bạn có thể sử dụng transform thuộc tính từ Container

    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(title: Text('Container.transform')),
        body: Container(
          height: 300,
          width: 300,
          transform: Matrix4.rotationZ(pi / 4),
          decoration: BoxDecoration(color: Colors.yellowAccent),
          child: Text(
            "Hi",
            textAlign: TextAlign.center,
          ),
        ),
      );
    }

    . . .

    BoxDecoration

    BoxDecoration thường được sử dụng trên tiện ích Container để thay đổi diện mạo của Container.

    image: DecorationImage

    Đặt một hình ảnh làm nền:

    Scaffold(
      appBar: AppBar(title: Text('image: DecorationImage')),
      body: Center(
        child: Container(
          height: 200,
          width: 200,
          decoration: BoxDecoration(
            color: Colors.yellow,
            image: DecorationImage(
              fit: BoxFit.fitWidth,
              image: NetworkImage(
                'https://flutter.io/images/catalog-widget-placeholder.png',
              ),
            ),
          ),
        ),
      ),
    );

    border: Border

    Chỉ định đường viền của Container sẽ trông như thế nào.

    Scaffold(
      appBar: AppBar(title: Text('border: Border')),
      body: Center(
        child: Container(
          height: 200,
          width: 200,
          decoration: BoxDecoration(
            color: Colors.yellow,
            border: Border.all(color: Colors.black, width: 3),
          ),
        ),
      ),
    );

    borderRadius: BorderRadius

    Cho phép làm tròn các góc của đường viền.

    borderRadius không hoạt động nếu shape trong decoration là BoxShape.circle

    Scaffold(
      appBar: AppBar(title: Text('borderRadius: BorderRadius')),
      body: Center(
        child: Container(
          height: 200,
          width: 200,
          decoration: BoxDecoration(
            color: Colors.yellow,
            border: Border.all(color: Colors.black, width: 3),
            borderRadius: BorderRadius.all(Radius.circular(18)),
          ),
        ),
      ),
    );

    shape: BoxShape

    BoxDecoration có thể là hình chữ nhật / hình vuông hoặc hình elip / hình tròn.

    Đối với bất kỳ hình dạng nào khác, bạn có thể sử dụng ShapeDecoration thay thế cho BoxDecoration

    Scaffold(
      appBar: AppBar(title: Text('shape: BoxShape')),
      body: Center(
        child: Container(
          height: 200,
          width: 200,
          decoration: BoxDecoration(
            color: Colors.yellow,
            shape: BoxShape.circle,
          ),
        ),
      ),
    );

    boxShadow: List<BoxShadow>

    Thêm bóng vào Container .

    Tham số này là một danh sách vì bạn có thể chỉ định nhiều bóng khác nhau và hợp nhất chúng lại với nhau.

    Scaffold(
      appBar: AppBar(title: Text('boxShadow: List<BoxShadow>')),
      body: Center(
        child: Container(
          height: 200,
          width: 200,
          decoration: BoxDecoration(
            color: Colors.yellow,
            boxShadow: const [
              BoxShadow(blurRadius: 10),
            ],
          ),
        ),
      ),
    );

    gradient

    Có ba loại gradient LinearGradient:, RadialGradientvà SweepGradient.

    Scaffold(
      appBar: AppBar(title: Text('gradient: LinearGradient')),
      body: Center(
        child: Container(
          height: 200,
          width: 200,
          decoration: BoxDecoration(
            gradient: LinearGradient(
              colors: const [
                Colors.red,
                Colors.blue,
              ],
            ),
          ),
        ),
      ),
    );

    . . .

    Scaffold(
      appBar: AppBar(title: Text('gradient: RadialGradient')),
      body: Center(
        child: Container(
          height: 200,
          width: 200,
          decoration: BoxDecoration(
            gradient: RadialGradient(
              colors: const [Colors.yellow, Colors.blue],
              stops: const [0.4, 1.0],
            ),
          ),
        ),
      ),
    );

    . . .

    Scaffold(
      appBar: AppBar(title: Text('gradient: SweepGradient')),
      body: Center(
        child: Container(
          height: 200,
          width: 200,
          decoration: BoxDecoration(
            gradient: SweepGradient(
              colors: const [
                Colors.blue,
                Colors.green,
                Colors.yellow,
                Colors.red,
                Colors.blue,
              ],
              stops: const [0.0, 0.25, 0.5, 0.75, 1.0],
            ),
          ),
        ),
      ),
    );

    backgroundBlendMode

    backgroundBlendMode là tài sản phức tạp nhất của BoxDecoration.
    Nó chịu trách nhiệm trộn các màu / độ chuyển màu với nhau BoxDecorationvà bất cứ thứ gì BoxDecorationở trên cùng.

    Với backgroundBlendModebạn có thể sử dụng một danh sách dài các thuật toán được chỉ định trong BlendModeenum.

    Đầu tiên, hãy đặt BoxDecoration là foregroundDecoration

    Scaffold(
      appBar: AppBar(title: Text('backgroundBlendMode')),
      body: Center(
        child: Container(
          height: 200,
          width: 200,
          foregroundDecoration: BoxDecoration(
            backgroundBlendMode: BlendMode.exclusion,
            gradient: LinearGradient(
              colors: const [
                Colors.red,
                Colors.blue,
              ],
            ),
          ),
          child: Image.network(
            'https://flutter.io/images/catalog-widget-placeholder.png',
          ),
        ),
      ),
    );

    . . .

    Material

    Đường viền với các góc cắt

    Scaffold(
      appBar: AppBar(title: Text('shape: BeveledRectangleBorder')),
      body: Center(
        child: Material(
          shape: const BeveledRectangleBorder(
            borderRadius: BorderRadius.all(Radius.circular(20)),
            side: BorderSide(color: Colors.black, width: 4),
          ),
          color: Colors.yellow,
          child: Container(
            height: 200,
            width: 200,
          ),
        ),
      ),
    );

    . . .

    Slivers

    SliverFillRemaining

    Widget này không thể thay thế khi bạn muốn căn giữa nội dung của mình ngay cả khi không có đủ dung lượng cho nó. Ví dụ

    Đủ không gian theo chiều dọc
    Scaffold(
      appBar: AppBar(title: Text('SliverFillRemaining')),
      body: CustomScrollView(
        slivers: [
          SliverFillRemaining(
            hasScrollBody: false,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: const [
                FlutterLogo(size: 200),
                Text(
                  'This is some longest text that should be centered'
                  'together with the logo',
                  textAlign: TextAlign.center,
                ),
              ],
            ),
          ),
        ],
      ),
    );

    Trong trường hợp không có đủ không gian cho nội dung được căn giữa, nội dung SliverFillRemaining sẽ có thể cuộn được:

    Nếu không có SliverFillRemaining nội dung sẽ tràn như thế này:

    Làm đầy không gian còn lại

    Ngoài việc hữu ích cho việc căn giữa nội dung của bạn, SliverFillRemaining nó sẽ lấp đầy không gian trống còn lại của khung nhìn. Để làm được điều đó, widget này phải được đặt vào CustomScrollView và cần phải là mảnh cuối cùng

    Trong trường hợp không có đủ dung lượng, tiện ích con sẽ có thể cuộn được:

    Scaffold(
      appBar: AppBar(title: Text('SliverFillRemaining')),
      body: CustomScrollView(
        slivers: [
          SliverList(
            delegate: SliverChildListDelegate(const [
              ListTile(title: Text('First item')),
              ListTile(title: Text('Second item')),
              ListTile(title: Text('Third item')),
              ListTile(title: Text('Fourth item')),
            ]),
          ),
          SliverFillRemaining(
            hasScrollBody: false,
            child: Container(
              color: Colors.yellowAccent,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: const [
                  FlutterLogo(size: 200),
                  Text(
                    'This is some longest text that should be centered'
                    'together with the logo',
                    textAlign: TextAlign.center,
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );

    . . .

    SizedBox

    Đây là một trong những Widget đơn giản nhưng hữu ích nhất

    SizedBox dưới dạng ConstrainedBox

    SizedBox có thể hoạt động theo cách tương tự như ConstrainedBox

    SizedBox.expand(
      child: Card(
        child: Text('Hello World!'),
        color: Colors.yellowAccent,
      ),
    ),

    . . .

    SizedBox dưới dạng padding

    Khi cần thêm phần đệm hoặc lề, bạn có thể chọn Padding hoặc Container các widget. Nhưng chúng có thể dài dòng hơn và khó đọc hơn so với việc thêm một Sizedbox

    Column(
      children: <Widget>[
        Icon(Icons.star, size: 50),
        const SizedBox(height: 100),
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
      ],
    ),

    SizedBox như một đối tượng vô hình

    Nhiều khi bạn muốn ẩn / hiện một tiện ích phụ thuộc vào bool

    Widget build(BuildContext context) {
      bool isVisible = ...
      return Scaffold(
        appBar: AppBar(
          title: Text('isVisible = $isVisible'),
        ),
        body: isVisible 
          ? Icon(Icons.star, size: 150) 
          : const SizedBox(),
      );
    }

    Vì SizedBox có một hàm const tạo nên việc sử dụng const SizedBox()thực sự rất tối ưu**.

    ** Một giải pháp tối ưu hơn sẽ là sử dụng Opacity widget và thay đổi opacity giá trị thành 0.0. Hạn chế của giải pháp này là tiện ích đã cho sẽ chỉ ẩn, vẫn chiếm không gian.

    . . .

    Trên các nền tảng khác nhau, có những khu vực đặc biệt như Thanh trạng thái trên Android hoặc Notch trên iPhone X mà chúng tôi có thể tránh vẽ dưới.

    Giải pháp cho vấn đề này là SafeArea widget (ví dụ không có / có SafeArea)

    Widget build(BuildContext context) {
      return Material(
        color: Colors.blue,
        child: SafeArea(
          child: SizedBox.expand(
            child: Card(color: Colors.yellowAccent),
          ),
        ),
      );
    }

    Kết thúc phần 3!

  • Giới thiệu FaunaDB

    FaunaDB là một lớp khác biệt với hầu hết các cơ sở dữ liệu xung quanh. Nó cung cấp tính linh hoạt và khả năng mở rộng đáng kinh ngạc. Vì FaunaDB không có máy chủ nên bạn có thể kết nối với cơ sở dữ liệu và sử dụng nó mà không cần lo lắng về bất kỳ điều gì khác.

    Bạn có thể sử dụng FaunaDB làm cơ sở dữ liệu OLTP (xử lý giao dịch trực tuyến) với ACID phân tán (tính nguyên tử, tính nhất quán, tính cách ly, độ bền), làm cơ sở dữ liệu tài liệu hoặc thậm chí là NoSQL dựa trên khóa-giá trị đơn giản. Nó cũng bao gồm hỗ trợ cho các tính năng doanh nghiệp như lưu giữ dữ liệu có thể định cấu hình và cho thuê nhiều lần theo thứ bậc.

    Quan trọng là, FaunaDB không ràng buộc bạn với đám mây. Nó có sẵn dưới dạng dịch vụ đám mây được quản lý hoặc JAR có thể tải xuống, hình ảnh máy hoặc vùng chứa mà bạn có thể chạy tại chỗ.

    Chức năng của FaunaDB

    FaunaDB cung cấp tính linh hoạt cao, cho phép bạn tinh chỉnh một số thông số dựa trên các yêu cầu của dự án của bạn. Chúng ta có thể sử dụng FaunaDB làm cơ sở dữ liệu quan hệ truyền thống, cơ sở dữ liệu khóa-giá trị, dựa trên tài liệu hoặc đồ thị. Chúng ta có thể thực thi một lược đồ trên dữ liệu hoặc chỉ để nó lỏng lẻo.

    FaunaDB là cực kỳ linh hoạt. Chúng ta có thể chạy FaunaDB trên đám mây (dưới dạng cơ sở dữ liệu không máy chủ với cấp miễn phí phong phú), tại cơ sở hoặc dưới dạng cơ sở dữ liệu nhúng, ngay bên trong ứng dụng của chúng ta. Cùng với những điểm cực đoan này, nó cũng cung cấp các hình thức triển khai phổ biến nhất như hình ảnh máy và hình ảnh docker.

    Tất cả điều này đi kèm với các giao dịch ACID và hiệu suất rất cao – mọi thứ mà một kiến trúc sư muốn cho một ứng dụng.

    Fauna Console

    Mặc dù Fauna cung cấp một trình bao lệnh thanh lịch cho đám mây DB (cơ sở dữ liệu), chúng ta hãy bắt đầu với bảng điều khiển web, đây là cách dễ dàng nhất để tham gia.

    Sau khi đăng ký, bạn sẽ thấy trang tổng quan giống như sau:

    Tạo cơ sở dữ liệu

    Để tạo cơ sở dữ liệu, bạn sẽ bắt đầu bằng cách nhấp vào Cơ sở dữ liệu mới (khá trực quan!). Sau khi bạn cung cấp tên DB, bạn đã hoàn tất; bây giờ bạn có một phiên bản DB mới cho ứng dụng của bạn. Nếu bạn chỉ muốn thực hành, bạn có thể chọn tùy chọn điền dữ liệu giả vào DB hoặc bạn có thể thêm dữ liệu của riêng mình.

    Với điều đó, bạn có một phiên bản cơ sở dữ liệu sạch.

    Tạo Collection.

    Tất nhiên, bước tiếp theo là tạo một bộ sưu tập. Fauna cung cấp một số lựa chọn khi chúng ta tạo một collection mới.

    Trang tạo collection trông giống như sau:

    Cùng với tên bộ sưu tập, chúng ta có hai tùy chọn. Ngày lịch sử và TTL (Thời gian tồn tại) cho mỗi bản ghi. Ngày lịch sử là số ngày Fauna sẽ lưu lại lịch sử của tài liệu (trong trường hợp bạn muốn hoàn tác thay đổi). Nếu chúng ta dọn sạch lĩnh vực này, lịch sử sẽ được lưu giữ mãi mãi. Đương nhiên, dữ liệu lịch sử cũng là dữ liệu được tính phí trên đám mây, vì vậy bạn phải cẩn thận khi chọn số này.

    TTL xác định số ngày mà một tài liệu chưa được chỉnh sửa ở lại trong bộ sưu tập sau lần viết cuối cùng. Việc đặt giá trị hợp lý cho TTL khá hữu ích vì nó có thể giúp giảm chi phí.

    Cuối cùng, chúng ta nhấn lưu và – bùng nổ – bạn đã tạo một bộ sưu tập mới. Bây giờ bạn sẽ thấy một giao diện người dùng sạch để thêm tài liệu mới vào bộ sưu tập.

    Tạo Document

    Nhấp vào nút đó sẽ hiển thị cho chúng ta một hộp văn bản nơi chúng ta có thể nhập JSON cho tài liệu mới mà chúng ta muốn thêm vào bộ sưu tập của mình.

    Bảng điều khiển cũng cung cấp một số tùy chọn khác để tạo chỉ mục, chức năng tùy chỉnh, thêm lược đồ GraphQL và cũng để tạo khóa bảo mật để truy cập cơ sở dữ liệu.

    Nếu bạn cảm thấy nhàm chán với giao diện người dùng, bảng điều khiển sẽ cung cấp liên kết để mở giao diện người dùng. Tại đây, chúng ta có thể gõ các lệnh cần thiết để làm việc với cơ sở dữ liệu. Trên thực tế, Fauna cũng cho phép chúng ta mở một trình bao trên thiết bị đầu cuối cục bộ của chúng ta để có thể tương tác với cơ sở dữ liệu. Hãy cùng kiểm tra nào.

    Fauna Shell

    Bất kỳ ai nghiêm túc về việc xây dựng các ứng dụng trong thế giới thực đều nên bỏ bảng điều khiển web và quay lại màn hình đen. Đó là cách tốt nhất để phát triển. Fauna cung cấp một CLI (giao diện dòng lệnh) rất tốt và một shell dựa trên NodeJS để giao tiếp với ứng dụng trên đám mây.

    Hãy bắt đầu với việc cài đặt. Nếu bạn đã hiểu được điều này, ta khá chắc chắn rằng bạn biết cách cài đặt NodeJS và npm (nếu chúng chưa được cài đặt). Vì vậy, hãy bắt đầu sau thời điểm đó. Chúng ta phải cài đặt Fauna shell bằng npm.

     Npm tập hợp tất cả những gì bạn cần để chạy Fauna shell và thiết lập nó.

    Tiếp theo, chúng ta đăng nhập vào tài khoản đám mây Fauna của mình.

    Điều này yêu cầu email và mật khẩu mà chúng ta đã sử dụng để đăng ký. Nó cũng có một tùy chọn để đăng nhập bằng một chìa khóa.

    QUERY DOCUMENTS

    Bây giờ, hãy thử tìm nạp các bản ghi từ bộ sưu tập. Có hai cách để truy vấn dữ liệu này. Bạn có thể sử dụng ID hoặc chỉ mục.

    Lưu ý trong các hoạt động tạo ở trên, chúng ta có một số – 270925464408687111 – trong xác nhận. Bạn có thể sử dụng điều này để truy vấn bản ghi.

    Điều này trả về bản ghi mà chúng ta vừa chèn.

    Tất nhiên, đây là một cách nhàm chán để truy vấn dữ liệu. Tại sao chúng ta tạo chỉ mục? Hãy sử dụng chỉ mục đó để truy vấn bản ghi được yêu cầu.

    Chúng ta có thể truy vấn cùng một bản ghi bằng cách sử dụng chỉ mục trên tiêu đề mà chúng ta đã tạo ở trên.

    Nó trả về cùng một bản ghi.

    Đó chỉ là một cái nhìn thoáng qua về những gì chúng ta có thể làm với bảng điều khiển web và shell. Fauna có tài liệu rất tốt. Bạn có thể xem Sách dạy nấu ăn trên trang web của họ để có một bộ sưu tập tốt các đoạn mã cho các trường hợp sử dụng khác nhau.

    Cảm ơn mọi người đã theo dõi.

    Author: DuongVT19

  • [Flutter] Bố cục Layout (Phần 2)

    [Flutter] Bố cục Layout (Phần 2)

    Tiếp tục phần trước, ta sẽ tiếp tục tìm hiểu thêm các widget giúp điều chỉnh bố cục hiển thị.

    IntrinsicWidth and IntrinsicHeight

    Tất cả tiện ích con bên trong Hàng hoặc Cột có chiều cao/rộng bằng tiện ích con cao nhất/rộng nhất

    Trong trường hợp bạn có kiểu bố trí này:

    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(title: Text('IntrinsicWidth')),
        body: Center(
          child: Column(
            children: <Widget>[
              RaisedButton(
                onPressed: () {},
                child: Text('Short'),
              ),
              RaisedButton(
                onPressed: () {},
                child: Text('A bit Longer'),
              ),
              RaisedButton(
                onPressed: () {},
                child: Text('The Longest text button'),
              ),
            ],
          ),
        ),
      );
    }

    Nhưng bạn muốn có tất cả các nút theo nút rộng nhất , chỉ cần sử dụng :IntrinsicWidth

    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(title: Text('IntrinsicWidth')),
        body: Center(
          child: IntrinsicWidth(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: <Widget>[
                RaisedButton(
                  onPressed: () {},
                  child: Text('Short'),
                ),
                RaisedButton(
                  onPressed: () {},
                  child: Text('A bit Longer'),
                ),
                RaisedButton(
                  onPressed: () {},
                  child: Text('The Longest text button'),
                ),
              ],
            ),
          ),
        ),
      );
    }

    Trong trường hợp bạn gặp vấn đề tương tự nhưng bạn muốn có tất cả các widget có chiều cao theo widget cao nhất chỉ cần sử dụng kết hợp của IntrinsicHeight và Row .

    IntrinsicWidthIntrinsicHeight còn được sử dụng để định kích thước con của nó theo chiều rộng/cao nội tại tối đa của nó. Nó có thể hữu ích nếu chiều rộng/cao khả dụng là không giới hạn, nhưng bạn muốn đặt kích thước của tiện ích con thành chiều rộng nội tại của nó.

    . . .

    Stack

    Chồng các Widget lên nhau

    @override
    Widget build(BuildContext context) {
      Widget main = Scaffold(
        appBar: AppBar(title: Text('Stack')),
      );
    
      return Stack(
        fit: StackFit.expand,
        children: <Widget>[
          main,
          Banner(
            message: "Top Start",
            location: BannerLocation.topStart,
          ),
          Banner(
            message: "Top End",
            location: BannerLocation.topEnd,
          ),
          Banner(
            message: "Bottom Start",
            location: BannerLocation.bottomStart,
          ),
          Banner(
            message: "Bottom End",
            location: BannerLocation.bottomEnd,
          ),
        ],
      );
    }

    . . .

    Bạn có thể các widget của mình vào Positioned Widget để xác định vị trí hiện thị

    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(title: Text('Stack')),
        body: Stack(
          fit: StackFit.expand,
          children: <Widget>[
            Material(color: Colors.yellowAccent),
            Positioned(
              top: 0,
              left: 0,
              child: Icon(Icons.star, size: 50),
            ),
            Positioned(
              top: 340,
              left: 250,
              child: Icon(Icons.call, size: 50),
            ),
          ],
        ),
      );
    }

    . . .

    Nếu bạn không muốn đoán các giá trị trên cùng/dưới cùng, bạn có thể sử dụng LayoutBuilder để lấy chúng

    Widget build(BuildContext context) {
      const iconSize = 50;
      return Scaffold(
        appBar: AppBar(title: Text('Stack with LayoutBuilder')),
        body: LayoutBuilder(
          builder: (context, constraints) =>
            Stack(
              fit: StackFit.expand,
              children: <Widget>[
                Material(color: Colors.yellowAccent),
                Positioned(
                  top: 0,
                  child: Icon(Icons.star, size: iconSize),
                ),
                Positioned(
                  top: constraints.maxHeight - iconSize,
                  left: constraints.maxWidth - iconSize,
                  child: Icon(Icons.call, size: iconSize),
                ),
              ],
            ),
        ),
      );
    }

    . . .

    Expanded

    Expanded hoạt động với bố cục Flex\Flexbox và rất tốt để phân phối không gian giữa nhiều mục.

    Row(
      children: <Widget>[
        Expanded(
          child: Container(
            decoration: const BoxDecoration(color: Colors.red),
          ),
          flex: 3,
        ),
        Expanded(
          child: Container(
            decoration: const BoxDecoration(color: Colors.green),
          ),
          flex: 2,
        ),
        Expanded(
          child: Container(
            decoration: const BoxDecoration(color: Colors.blue),
          ),
          flex: 1,
        ),
      ],
    ),

    . . .

    ConstrainedBox

    Theo mặc định, hầu hết các tiện ích con sẽ sử dụng ít dung lượng nhất có thể:

    Card(child: const Text('Hello World!'), color: Colors.yellow)

    . . .

    ConstrainedBox cho phép tiện ích con sử dụng không gian còn lại như mong muốn.

    ConstrainedBox( 
      constraints: BoxConstraints.expand(),
      child: const Card(
        child: const Text('Hello World!'), 
        color: Colors.yellow,
      ), 
    ),

    . . .

    Bằng cách sử dụng BoxConstraints, bạn chỉ định lượng không gian mà một widget có thể có – bạn chỉ định minmax của heightwidth.

    BoxConstraints.expand sử dụng lượng không gian vô hạn (tất cả khả dụng) trừ khi được chỉ định:

    ConstrainedBox(
      constraints: BoxConstraints.expand(height: 300),
      child: const Card(
        child: const Text('Hello World!'), 
        color: Colors.yellow,
      ),
    ),

    Và nó giống như:

    ConstrainedBox(
      constraints: BoxConstraints(
        minWidth: double.infinity,
        maxWidth: double.infinity,
        minHeight: 300,
        maxHeight: 300,
      ),
      child: const Card(
        child: const Text('Hello World!'), 
        color: Colors.yellow,
      ),
    ),

    . . .

    Align

    Đôi khi bạn gặp khó khăn trong việc đặt tiện ích con của chúng tôi ở kích thước phù hợp – ví dụ: nó liên tục bị kéo căng khi bạn không muốn:

    Ví dụ ở trên xảy ra khi bạn có Column với CrossAxisAlignment.stretch và bạn chỉ muốn nút không bị kéo căng:

    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(title: Text('Align: without Align')),
        body: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Align(
              child: RaisedButton(
                onPressed: () {},
                child: const Text('Button'),
              ),
            ),
          ],
        ),
      );
    }

    Khi tiện ích con của bạn không tuân theo các ràng buộc mà bạn cố gắng thiết lập, trước tiên hãy thử kết hợp với nó Align

    Kết thúc phần 2!

    . . .

  • [Flutter] Bố cục Layout (Phần 1)

    [Flutter] Bố cục Layout (Phần 1)

    Bạn cần các mẫu bố cục đơn giản cho Flutter?
    Tôi giới thiệu cho bạn tập hợp các đoạn mã bố cục Flutter của tôi. Tôi sẽ giữ cho nó ngắn gọn, dễ hiểu và đơn giản với vô số ví dụ trực quan.

    Row và Column

    MainAxisAlignment

    Row /*or Column*/( 
      mainAxisAlignment: MainAxisAlignment.start,
      children: <Widget>[
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
      ],
    ),

    . . .

    Row /*or Column*/( 
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
      ],
    ),

    . . .

    Row /*or Column*/( 
      mainAxisAlignment: MainAxisAlignment.end,
      children: <Widget>[
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
      ],
    ),

    . . .

    Row /*or Column*/( 
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: <Widget>[
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
      ],
    ),

    . . .

    Row /*or Column*/( 
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: <Widget>[
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
      ],
    ),

    . . .

    Row /*or Column*/( 
      mainAxisAlignment: MainAxisAlignment.spaceAround,
      children: <Widget>[
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
      ],
    ),

    . . .

    CrossAxisAlignment

    Bạn nên sử dụng CrossAxisAlignment.baseline nếu bạn yêu cầu căn chỉnh đường cơ sở của các văn bản khác nhau

    Row(
      crossAxisAlignment: CrossAxisAlignment.baseline,
      textBaseline: TextBaseline.alphabetic,
      children: <Widget>[
        Text(
          'Baseline',
          style: Theme.of(context).textTheme.display3,
        ),
        Text(
          'Baseline',
          style: Theme.of(context).textTheme.body1,
        ),
      ],
    ),

    . . .

    Row /*or Column*/( 
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 200),
        Icon(Icons.star, size: 50),
      ],
    ),

    . . .

    Row /*or Column*/( 
      crossAxisAlignment: CrossAxisAlignment.center,
      children: <Widget>[
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 200),
        Icon(Icons.star, size: 50),
      ],
    ),

    . . .

    Row /*or Column*/( 
      crossAxisAlignment: CrossAxisAlignment.end,
      children: <Widget>[
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 200),
        Icon(Icons.star, size: 50),
      ],
    ),

    . . .

    Row /*or Column*/( 
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: <Widget>[
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 200),
        Icon(Icons.star, size: 50),
      ],
    ),

    Kết thúc phần 1!