Flutter 웹 앱에서 플러그인 사용

Flutter는 하나의 코드베이스를 사용해 모바일, 웹, 데스크톱을 대상으로 아름다운 네이티브 컴파일 애플리케이션을 개발하기 위한 Google의 UI 도구 모음입니다. 이 Codelab에서는 GitHub 저장소의 별표 수를 보고하는 앱을 완성합니다. Dart DevTools를 사용하여 간단한 디버깅을 합니다. Firebase에서 앱을 호스팅하는 방법을 알아봅니다. 마지막으로 Flutter 플러그인을 사용하여 앱을 실행하고 호스팅된 개인정보처리방침을 엽니다.

학습할 내용

  • 웹 앱에서 Flutter 플러그인을 사용하는 방법
  • 패키지와 플러그인의 차이점
  • Dart DevTools를 사용하여 웹 앱을 디버그하는 방법
  • Firebase에서 앱을 호스팅하는 방법

기본 요건: 이 Codelab은 개발자가 기본 Flutter 지식이 있다고 가정합니다. Flutter를 처음 사용하는 경우 웹에서 첫 번째 Flutter 앱 작성부터 시작하는 것이 좋습니다.

이 Codelab에서 학습하고 싶은 내용은 무엇인가요?

주제를 처음 접하기 때문에 간단하게 내용을 파악하고 싶습니다. 이 주제에 관해 약간 알고 있지만 한 번 더 확인하고 싶습니다. 프로젝트에 사용할 예시 코드를 찾고 있습니다. 구체적인 항목에 관한 설명을 찾고 있습니다.

플러그인(플러그인 패키지라고도 함)은 하나 이상의 플랫폼별 구현과 결합된 Dart 코드로 작성된 API를 포함하는 특수 Dart 패키지입니다. 플러그인 패키지는 Android(Kotlin 또는 자바 사용), iOS(Swift 또는 Objective-C 사용), (Dart 사용), macOS(Dart 사용) 또는 앞서 언급된 항목의 조합으로 작성할 수 있습니다. 사실 Flutter는 제휴 플러그인을 지원하므로 이를 통해 여러 플랫폼에서 패키지를 사용할 수 있습니다.

패키지는 앱의 기능을 확장하거나 단순화하는 데 사용할 수 있는 Dart 라이브러리입니다. 앞서 언급했듯이 플러그인은 패키지의 한 유형입니다. 패키지 및 플러그인에 관한 자세한 정보는 Flutter 플러그인 또는 Dart 패키지를 참고하세요.

이 실습을 완료하려면 Flutter SDK, 편집기, Chrome 브라우저라는 세 가지 소프트웨어가 필요합니다. Android 스튜디오 또는 Flutter 및 Dart 플러그인이 설치된 IntelliJ와 같이 선호하는 편집기를 사용하거나 Dart 코드 및 Flutter 확장 프로그램이 포함된 Visual Studio Code를 사용해도 됩니다. Chrome에서 Dart DevTools를 사용하여 코드를 디버그합니다.

이 Codelab의 경우 흥미로운 부분에 빠르게 다다를 수 있도록 많은 시작 코드를 제공합니다.

b2f84ff91b0e1396.png 간단한 템플릿 Flutter 앱을 만듭니다.

star_counter라는 Flutter 프로젝트를 만들고 다음과 같이 null 안전으로 이전합니다.

$ flutter create star_counter
$ cd star_counter
$ dart migrate --apply-changes

b2f84ff91b0e1396.png pubspec.yaml 파일을 업데이트합니다. 프로젝트 상단에서 pubspec.yaml 파일을 업데이트합니다.

pubspec.yaml

name: star_counter
description: A GitHub Star Counter app
version: 1.0.0+1
environment:
  sdk: ">=2.12.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
  flutter_markdown: ^0.6.0
  github: ^8.0.0
  intl: ^0.17.0

dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:
  uses-material-design: true

b2f84ff91b0e1396.png 업데이트된 종속 항목을 가져옵니다. IDE에서 Pub get 버튼을 클릭하거나 명령줄에서 프로젝트 상단의 flutter pub get을 실행합니다.

b2f84ff91b0e1396.png lib/main.dart의 콘텐츠를 바꿉니다. 머티리얼 테마의 count-the-number-of-button-presses 앱을 만드는 lib/main.dart의 코드를 모두 삭제합니다. 아직 완성되지 않은 count-the-number-of-stars-on-a-GitHub-repo 앱을 설정하는 다음 코드를 추가합니다.

lib/main.dart

import 'package:flutter/material.dart';

void main() {
  runApp(StarCounterApp());
}

class StarCounterApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        brightness: Brightness.light,
      ),
      routes: {
        '/': (context) => HomePage(),
      },
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  String _repositoryName = "";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ConstrainedBox(
          constraints: BoxConstraints(maxWidth: 400),
          child: Card(
            child: Padding(
              padding: EdgeInsets.all(16.0),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  Text(
                    'GitHub Star Counter',
                    style: Theme.of(context).textTheme.headline4,
                  ),
                  TextField(
                    decoration: InputDecoration(
                      labelText: 'Enter a GitHub repository',
                      hintText: 'flutter/flutter',
                    ),
                    onSubmitted: (text) {
                      setState(() {
                        _repositoryName = text;
                      });
                    },
                  ),
                  Padding(
                    padding: const EdgeInsets.only(top: 32.0),
                    child: Text(
                      _repositoryName,
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

b2f84ff91b0e1396.png 앱을 실행합니다. Chrome에서 앱을 실행합니다. IDE를 사용한다면 먼저 기기 풀다운에서 Chrome을 선택합니다. 명령줄을 사용한다면 패키지 상단에서 flutter run -d chrome을 실행합니다. 웹이 구성되었지만 다른 연결된 기기가 없다고 flutter devices에 표시되면 flutter run 명령어는 기본적으로 Chrome으로 설정됩니다.

Chrome이 실행되면 다음과 같이 표시됩니다.

97cb2368f34eb03c.png

텍스트 입력란에 텍스트를 입력한 다음 리턴 키를 누릅니다. 입력한 텍스트가 창 하단에 표시됩니다.

다음으로 'google/flutter.widgets' 양식에 입력한 텍스트를 표시하는 대신 저장소의 별표 수를 표시하도록 앱을 수정합니다.

b2f84ff91b0e1396.png libstar_counter.dart라는 새 파일을 만듭니다.

lib/star_counter.dart

import 'package:flutter/material.dart';
import 'package:github/github.dart';
import 'package:intl/intl.dart' as intl;

class GitHubStarCounter extends StatefulWidget {
  /// The full repository name, e.g. torvalds/linux
  final String repositoryName;

  GitHubStarCounter({
    required this.repositoryName,
  });

  @override
  _GitHubStarCounterState createState() => _GitHubStarCounterState();
}

class _GitHubStarCounterState extends State<GitHubStarCounter> {
  // The GitHub API client
  late GitHub github;

  // The repository information
  Repository? repository;

  // A human-readable error when the repository isn't found.
  String? errorMessage;

  void initState() {
    super.initState();
    github = GitHub();

    fetchRepository();
  }

  void didUpdateWidget(GitHubStarCounter oldWidget) {
    super.didUpdateWidget(oldWidget);

    // When this widget's [repositoryName] changes,
    // load the Repository information.
    if (widget.repositoryName == oldWidget.repositoryName) {
      return;
    }

    fetchRepository();
  }

  Future<void> fetchRepository() async {
    setState(() {
      repository = null;
      errorMessage = null;
    });

    if (widget.repositoryName.isNotEmpty) {
      var repo = await github.repositories
          .getRepository(RepositorySlug.full(widget.repositoryName));
      setState(() {
        repository = repo;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    final textTheme = Theme.of(context).textTheme;
    final textStyle = textTheme.headline4?.apply(color: Colors.green);
    final errorStyle = textTheme.bodyText1?.apply(color: Colors.red);
    final numberFormat = intl.NumberFormat.decimalPattern();

    if (errorMessage != null) {
      return Text(errorMessage!, style: errorStyle);
    }

    if (widget.repositoryName.isNotEmpty && repository == null) {
      return Text('loading...');
    }

    if (repository == null) {
      // If no repository is entered, return an empty widget.
      return SizedBox();
    }

    return Text(
      '${numberFormat.format(repository!.stargazersCount)}',
      style: textStyle,
    );
  }
}

cf1e10b838bf60ee.png 유용한 정보

  • Star Counter는 github Dart 패키지를 사용하여 저장소가 획득한 별표 수를 GitHub에 쿼리합니다.
  • pub.dev에서 패키지와 플러그인을 확인할 수 있습니다.
  • 특정 플랫폼의 패키지를 둘러보고 검색할 수도 있습니다. 방문 페이지에서 FLUTTER를 선택하면 다음 페이지에서 을 선택합니다. 그러면 웹에서 실행되는 패키지가 모두 표시됩니다. 패키지의 페이지를 둘러보거나 검색창을 사용하여 검색결과 범위를 좁힙니다.
  • Flutter 커뮤니티는 pub.dev에 패키지와 플러그인을 제공합니다. github 패키지의 페이지를 보면 WEB을 포함하여 거의 모든 Dart 또는 Flutter 앱에서 작동한다는 것을 알 수 있습니다.
  • Flutter 즐겨찾기로 표시된 패키지에 특히 주의를 기울일 수 있습니다. Flutter 즐겨찾기 프로그램은 기능 완전성 및 우수한 런타임 동작과 같은 특정 기준을 충족하는 패키지를 식별합니다.
  • 나중에 pub.dev의 플러그인을 이 예시에 추가합니다.

b2f84ff91b0e1396.png 다음 importmain.dart에 추가합니다.

lib/main.dart

import 'star_counter.dart';

b2f84ff91b0e1396.pngGitHubStarCounter 위젯을 사용합니다.

main.dart에서 Text 위젯(60~62행)을 GitHubStarCounterWidget을 정의하는 새로운 행 3개로 바꿉니다.

lib/main.dart

Padding(
  padding: const EdgeInsets.only(top: 32.0),
  child: GitHubStarCounter(              // New
    repositoryName: _repositoryName,     // New
  ),                                     // New
),

b2f84ff91b0e1396.png 앱을 실행합니다.

IDE에서 실행 버튼을 다시 클릭하거나(앱을 먼저 중지하지 않고) 핫 리스타트 버튼(293160db29e53878.png)을 클릭하거나 콘솔에 r을 입력하여 앱을 핫 리스타트합니다. 이렇게 하면 브라우저를 새로고침하지 않고 앱이 업데이트됩니다.

창은 이전과 동일하게 표시됩니다. 제안된 flutter/flutter와 같은 기존 저장소를 입력합니다. 별표 수는 다음 예와 같이 텍스트 입력란 아래에 보고됩니다.

78a5f531b1acfd58.png

이제 디버깅 연습을 해보겠습니다. 실행 중인 앱에서 존재하지 않는 저장소(예: foo/bar)를 입력합니다. '로드 중...'이라고 위젯에 계속 표시됩니다. 지금 이 문제를 해결하세요.

b2f84ff91b0e1396.png Dart DevTools를 실행합니다.

Chrome DevTools에 익숙할 수 있지만 Flutter 앱을 디버그하려면 Dart DevTools를 사용하는 것이 좋습니다. Dart DevTools는 Dart 및 Flutter 앱을 디버그하고 프로파일링하도록 설계되었습니다. 워크플로에 따라 Dart DevTools를 실행하는 방법은 다양합니다. 다음 페이지에는 DevTools를 설치하고 실행하는 방법에 대한 안내가 있습니다.

b2f84ff91b0e1396.png 디버거를 불러옵니다.

Dart DevTools를 실행할 때 표시되는 초기 브라우저 페이지는 실행 방법에 따라 다를 수 있습니다. 디버거3d8c8053deda4caa.png을 클릭하여 디버거를 불러옵니다.

b2f84ff91b0e1396.png star_counter.dart 소스 코드를 불러옵니다.

라이브러리 텍스트 입력란의 왼쪽 하단에 star_counter를 입력합니다. 결과 목록에서 package:star_counter/star_counter.dart 항목을 더블클릭하여 파일 뷰에서 엽니다.

b2f84ff91b0e1396.png 중단점을 설정합니다.

소스에서 var repo = await github.repositories 행을 찾습니다. 52행에 있어야 합니다. 행 번호의 왼쪽을 클릭하면 중단점이 설정되었음을 나타내는 원이 표시됩니다. 중단점은 왼쪽의 중단점 목록에도 표시됩니다. 오른쪽 상단에서 예외 시 중단 체크박스를 선택합니다. UI가 다음과 같이 표시됩니다.

eeec16d42e7012ba.png

b2f84ff91b0e1396.png 앱을 실행합니다.

존재하지 않는 저장소를 입력하고 리턴 키를 누릅니다. 오류 창의 코드 창 아래에 github 패키지에서 '저장소를 찾을 수 없음' 예외가 발생했다고 표시됩니다.

Error: GitHub Error: Repository Not Found: /
    at Object.throw_ [as throw] (http://localhost:52956/dart_sdk.js:4463:11)
    at http://localhost:52956/packages/github/src/common/xplat_common.dart.lib.js:1351:25
    at github.GitHub.new.request (http://localhost:52956/packages/github/src/common/xplat_common.dart.lib.js:10679:13)
    at request.next (<anonymous>)
    at http://localhost:52956/dart_sdk.js:37175:33
    at _RootZone.runUnary (http://localhost:52956/dart_sdk.js:37029:58)
    at _FutureListener.thenAwait.handleValue (http://localhost:52956/dart_sdk.js:32116:29)
    at handleValueCallback (http://localhost:52956/dart_sdk.js:32663:49)
    at Function._propagateToListeners (http://localhost:52956/dart_sdk.js:32701:17)
    at _Future.new.[_completeWithValue] (http://localhost:52956/dart_sdk.js:32544:23)
    at async._AsyncCallbackEntry.new.callback (http://localhost:52956/dart_sdk.js:32566:35)
    at Object._microtaskLoop (http://localhost:52956/dart_sdk.js:37290:13)
    at _startMicrotaskLoop (http://localhost:52956/dart_sdk.js:37296:13)
    at http://localhost:52956/dart_sdk.js:32918:9

b2f84ff91b0e1396.png 오류를 포착합니다.

star_counter.dart에서 다음 코드를 찾습니다(52~58행).

if (widget.repositoryName.isNotEmpty) {
  var repo = await github.repositories
      .getRepository(RepositorySlug.full(widget.repositoryName));
  setState(() {
    repository = repo;
  });
}

이 코드를 try-catch 블록을 사용하는 코드로 바꿔 오류를 포착하고 메시지를 출력하여 더 원활하게 작동하도록 합니다.

lib/star_counter.dart

if (widget.repositoryName.isNotEmpty) {
  try {
    var repo = await github.repositories
        .getRepository(RepositorySlug.full(widget.repositoryName));
    setState(() {
      repository = repo;
    });
  } on RepositoryNotFound {
    setState(() {
      repository = null;
      errorMessage = '${widget.repositoryName} not found.';
    });
  }
}

b2f84ff91b0e1396.png 앱을 핫 리스타트합니다.

DevTools에서 소스 코드가 변경사항을 반영하도록 업데이트됩니다. 다시 한번 존재하지 않는 저장소를 입력합니다. 아래와 같이 표시됩니다.

f1b3847ee101a85b.png

f5077295022a18df.png특별한 성과를 이루었습니다.

이 단계에서는 개인정보처리방침 페이지를 앱에 추가합니다. 먼저 Dart 코드에 개인정보처리방침 텍스트를 삽입합니다.

b2f84ff91b0e1396.png lib/privacy_policy.dart 파일을 추가합니다. lib 디렉터리에서 privacy_policy.dart 파일을 프로젝트에 추가합니다.

lib/privacy_policy.dart

import 'package:flutter/widgets.dart';
import 'package:flutter_markdown/flutter_markdown.dart';

class PrivacyPolicy extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Markdown(
      data: _privacyPolicyText,
    );
  }
}

// The source for this privacy policy was generated by
// https://app-privacy-policy-generator.firebaseapp.com/
var _privacyPolicyText = '''
## Privacy Policy

Flutter Example Company built the Star Counter app as an Open Source app. This SERVICE is provided by Flutter Example Company at no cost and is intended for use as is.

This page is used to inform visitors regarding our policies with the collection, use, and disclosure of Personal Information if anyone decided to use our Service.

If you choose to use our Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that we collect is used for providing and improving the Service. We will not use or share your information with anyone except as described in this Privacy Policy.

The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which is accessible at Star Counter unless otherwise defined in this Privacy Policy.
''';

b2f84ff91b0e1396.png 다음 importmain.dart에 추가합니다.

lib/main.dart

import 'privacy_policy.dart';

b2f84ff91b0e1396.png 개인정보처리방침의 새 경로(페이지)를 추가합니다.

17행 뒤에 개인정보처리방침 페이지 경로를 추가합니다.

lib/main.dart

routes: {
  '/': (context) => HomePage(),
  '/privacypolicy': (context) => PrivacyPolicy(),  // NEW
},

b2f84ff91b0e1396.png 개인정보처리방침을 표시하는 버튼을 추가합니다.

_HomePageStatebuild() 메서드에서 65행 뒤 Column 하단에 TextButton을 추가합니다.

lib/main.dart

TextButton(
  style: ButtonStyle(
    foregroundColor: MaterialStateProperty.all(Colors.blue),
    overlayColor: MaterialStateProperty.all(Colors.transparent),
  ),
  onPressed: () => Navigator.of(context).pushNamed('/privacypolicy'),
  child: Text('Privacy Policy'),
),

b2f84ff91b0e1396.png 앱을 실행합니다.

앱을 핫 리스타트합니다. 이제 화면 하단에 개인정보처리방침 링크가 있습니다.

ae990c7f6e0918e5.png

b2f84ff91b0e1396.png 개인정보처리방침 버튼을 클릭합니다.

개인정보처리방침이 표시되고 URL은 /privacypolicy로 변경됩니다.

c233a1dea9abfaec.png

b2f84ff91b0e1396.png 뒤로 이동합니다.

브라우저의 뒤로 버튼을 사용하여 첫 번째 페이지로 돌아갑니다. 쉽게 이 동작을 구현할 수 있습니다.

호스팅된 페이지의 장점은 새 버전의 앱을 출시하지 않고도 페이지를 변경할 수 있다는 것입니다.

프로젝트 루트에 있는 명령줄에서 다음 안내를 따르세요.

b2f84ff91b0e1396.png Firebase CLI를 설치합니다.

b2f84ff91b0e1396.png firebase login을 사용하여 Firebase에 로그인하여 인증합니다.

b2f84ff91b0e1396.png firebase init.를 사용하여 Firebase 프로젝트를 초기화합니다.

다음 값을 사용합니다.

  • 사용할 Firebase 기능은 무엇인가요? 호스팅
  • 프로젝트 설정: 새 프로젝트 만들기
  • 프로젝트 이름이 무엇인가요? [yourname]-my-flutter-app(고유해야 함)
  • 프로젝트를 어떻게 부르나요? 리턴 키를 눌러 기본값(이전 질문에 사용된 이름과 동일)을 적용합니다.
  • 공개 디렉터리는 무엇인가요? build/web(중요)
  • 단일 페이지 앱으로 구성하나요?
  • GitHub로 자동 빌드 및 배포를 설정하나요? 아니요

firebase init 실행이 완료되면 명령줄에 다음과 같은 내용이 표시됩니다.

55135b9eda3c41ef.png

init 명령어가 완료되면 다음 파일이 프로젝트에 추가됩니다.

  • 구성 파일 firebase.json
  • 프로젝트 데이터가 포함된 .firebaserc

firebase.jsonpublic 입력란에서 build/web을 지정하는지 확인합니다. 예를 들면 다음과 같습니다.

firebase.json

{
  "hosting": {
    "public": "build/web",    # This is important!
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  }
}

b2f84ff91b0e1396.png 앱의 출시 버전을 빌드합니다.

다음 방법 중 하나를 사용하여 앱의 출시 버전을 빌드하도록 IDE를 구성합니다.

  • Android 스튜디오 또는 IntelliJ에서 Run > Edit Configuration 대화상자의 Additional arguments 입력란에 --release를 지정합니다. 그런 다음 앱을 실행하세요.
  • 명령줄에서 flutter build web --release를 실행합니다.

프로젝트의 build/web 디렉터리를 검사하여 이 단계가 작동하는지 확인합니다. 디렉터리에는 index.html을 비롯한 여러 파일이 포함되어야 합니다.

b2f84ff91b0e1396.png 앱을 배포합니다.

명령줄에서 프로젝트 상단의 firebase deploy를 실행하여 공개 build/web 디렉터리의 콘텐츠를 배포합니다. 그러면 호스팅된 URL이 표시됩니다. https://project-id>.web.app.

브라우저에서 https://<project-id>.web.app 또는 https://<project-id>.web.app/#/privacypolicy로 이동하여 실행 중인 개인정보처리방침 버전을 확인합니다.

다음으로 Dart 코드에 개인정보처리방침을 삽입하는 대신 Firebase를 사용하여 HTML 페이지로 개인정보처리방침을 호스팅합니다.

b2f84ff91b0e1396.png privacy_policy.dart.를 삭제합니다.

프로젝트의 lib 디렉터리에서 파일을 삭제합니다.

b2f84ff91b0e1396.png main.dart.를 업데이트합니다.

lib/main.dart에서 하단의 import 문 import privacy_policy.dartPrivacyPolicyPage 위젯을 삭제합니다.

b2f84ff91b0e1396.png privacy_policy.html.을 추가합니다.

이 파일을 프로젝트의 web 디렉터리에 배치합니다.

web/privacy_policy.html

<!doctype html>

<html lang="en">
<head>
  <meta charset="utf-8">

  <title>Privacy Policy</title>
</head>

<body>
<h2 id="privacy-policy">Privacy Policy</h2>
<p>Flutter Example Company built the Star Counter app as an Open Source app. This SERVICE is provided by Flutter Example Company at no cost and is intended for use as is.</p>
<p>This page is used to inform visitors regarding our policies with the collection, use, and disclosure of Personal Information if anyone decided to use our Service.</p>
<p>If you choose to use our Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that we collect is used for providing and improving the Service. We will not use or share your information with anyone except as described in this Privacy Policy.</p>
<p>The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which is accessible at Star Counter unless otherwise defined in this Privacy Policy.</p>
</body>
</html>

다음으로 url_launcher 플러그인을 사용하여 새 탭에서 개인정보처리방침을 엽니다.

Flutter 웹 앱은 단일 페이지 앱(SPA)입니다. 이로 인해 웹 예시에서 표준 라우팅 메커니즘을 사용하면 동일한 웹페이지에서 개인정보처리방침이 열립니다. 반면에 URL 런처 플러그인은 브라우저에서 새 탭을 열고 앱의 다른 사본을 실행하며 앱을 호스팅된 페이지로 라우팅합니다.

b2f84ff91b0e1396.png 종속 항목을 추가합니다.

pubspec.yaml 파일에서 다음 종속 항목을 추가합니다. YAML 파일에서 공백이 중요하므로 행이 공백 2개로 시작하는지 확인합니다.

pubspec.yaml

  url_launcher: ^6.0.0

b2f84ff91b0e1396.png 새 종속 항목을 가져옵니다.

종속 항목을 추가하려면 앱을 완전히 다시 시작해야 하므로 앱을 중지합니다. IDE에서 Pub 가져오기 버튼을 클릭하거나 명령줄에서 프로젝트 상단의 flutter pub get을 실행합니다.

b2f84ff91b0e1396.png 다음 importmain.dart에 추가합니다.

lib/main.dart

import 'package:url_launcher/url_launcher.dart';

b2f84ff91b0e1396.png TextButton의 핸들러를 업데이트합니다.

또한 main.dart에서 사용자가 개인정보처리방침 버튼을 누를 때 호출되는 코드를 바꿉니다. 원본 코드(71행)는 Flutter의 일반 라우팅 메커니즘을 사용합니다.

onPressed: () => Navigator.of(context).pushNamed('/privacypolicy'),

onPressed의 새 코드가 url_launcher 패키지를 호출합니다.

lib/main.dart

onPressed: () => launch(
  '/privacy_policy.html',
  enableJavaScript: true,
  enableDomStorage: true,
),

b2f84ff91b0e1396.png 앱을 실행합니다.

개인정보처리방침 버튼을 클릭하여 새 탭에서 파일을 엽니다. url_launcher 패키지를 사용하는 주요 이점은 개인정보처리방침 페이지가 호스팅된 후 웹 모바일 플랫폼에서 작동한다는 것입니다. 또 다른 이점은 앱을 다시 컴파일하지 않고도 호스팅된 개인정보처리방침 페이지를 수정할 수 있다는 것입니다.

프로젝트를 완료한 후에는 다음과 같이 정리해야 합니다.

b2f84ff91b0e1396.png Firebase 프로젝트를 삭제합니다.

축하합니다. GitHub Star Counter 앱을 성공적으로 완료했습니다. Dart DevTools에 관해서도 간단히 알아봤으며, 이 도구는 웹 앱뿐만 아니라 모든 Dart 및 Flutter 앱을 디버그하고 프로파일링하는 데 사용할 수 있습니다.

다음 단계

Flutter에 관해 계속 알아보세요.