Pull to refresh

Flutter: Создание расширения для Chrome

Reading time 6 min
Views 5K
Original author: Melih Arik

Как создать расширение для браузера? Создание расширения для Google Chrome.

Сложность: Опытный

Вступление

Всем привет. Месяц назад, пока бороздил просторы интернета, понял, что у меня есть проблема, я хотел проверить текущую цену Биткойна, но каждый раз заходить на сайт валюты мне было некомфортно. Итак, я решил сделать расширение для Google Chrome с помощью Flutter. И хочу рассказать как я это сделал.

Расширение будет достаточно простым, будет лишь функционал проверки состояния Биткойна. Вам не понадобится дополнительная установка каких-либо плагинов. Мы напишем его с помощью встроенных средств web.

Ваш Flutter SDK должен быть обновлен до последней версии. Если нет, у вас могут быть некоторые проблемы, поскольку веб-поддержка — довольно новая возможность во Flutter. А также убедитесь, что на вашем устройстве уже установлен Google Chrome.

В этой статье я предполагаю, что вы уже имеете некоторое представление о Flutter и его виджетах. Если нет, то рекомендую ознакомиться с ним чуть больше, вот ссылка на документацию.

Содержание

Раздел 1: Создание проекта

Создаем новый Flutter-проект, я его назвал "chrome_extension":

Прежде всего, нам нужно создать наше приложение для веб. Измените свое устройство на Chrome из нижней панели:

После того как мы это сделали, наше приложение работает в Chrome без ошибок:

Теперь перейдите к файлу index.html. Очистите тегbody. Затем создайте новый тег script внутри тега body и укажите атрибуты type и src.

Затем вам нужно указать высоту и ширину внутри тега html. Если нет, расширение будет создано с нулевой высотой и нулевой шириной. В итоге ваш код файла index.html должен быть похож на этот:

<!DOCTYPE html>
<html style="height: 650px; width: 300px;">
<head>
  <!--
	   Если вы обслуживаете свое веб-приложение по пути, отличному от корневого, измените
     href ниже, чтобы обращаться по нужному пути.

     Больше подробностей:
     * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base

     $FLUTTER_BASE_HREF — это заполнитель для базового href, 
		 который в дальнейшем будет заменен значением.
     Аргумент `--base-href`, при использовании команды "flutter build".
  -->
  <base href="$FLUTTER_BASE_HREF">

  <meta charset="UTF-8">
  <meta content="IE=Edge" http-equiv="X-UA-Compatible">
  <meta name="description" content="A new Flutter project.">

  <!-- iOS мета-теги и иконки -->
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="apple-mobile-web-app-title" content="chrome_extension">
  <link rel="apple-touch-icon" href="icons/Icon-192.png">

  <!-- Favicon -->
  <link rel="icon" type="image/png" href="favicon.png"/>

  <title>chrome_extension</title>
  <link rel="manifest" href="manifest.json">
</head>
<body>
  <script type="application/javascript" src="main.dart.js"></script>
</body>
</html>

Следующее, что нужно сделать, это перейти к файлу manifest.json. Здесь вы указываете то, как хотите чтобы ваше расширение себя вело. Мы можем удалить все это и создать снова. Потому что нам нужны не все из них:

{
    "name": "First Chrome Extension Name",
    "short_name": "First Chrome Extension Short Name",
    "description": "Chrome Extension description",
    "version": "1.0",
    "action": {
        "default_popup": "index.html",
        "default_icon": "/icons/Icon-192.png"
    },
    "manifest_version": 3
}

В файле manifest.json есть много полей. Вы можете прочитать больше по этой ссылке.

Теперь все установлено. Нам нужно собрать версию нашего проекта. Скопируйте это и вставьте в свой терминал:

flutter build web --web-renderer html --csp

Если вы хотите знать, для чего мы пишем этот код, вы можете узнать больше, написав:

flutter build web -h

Теперь нам нужно загрузить наше расширение в Chrome. Откройте Chrome и перейдите к своим расширениям: chrome://extensions/

Убедитесь, что вы включили режим разработчика в верхнем правом углу.

Нажмите на кнопку «Загрузить распакованное» и добавьте папку web в папку build.

После этих действий расширение появилось:

Исходный код полного приложения доступен на Github.

Раздел 2: Биткойн-трекер

Пришло время создавать настоящее расширение. Мы будем использовать nomics.com API для текущих цен на валюту. Вы можете получить ключ API по этой ссылке.

Нам нужно получить данные из API. Этот класс CryptoApi получает данные и возвращает их как List<Currency>.

import 'dart:convert';
import 'package:extension_example/model/currency.dart';
import 'package:http/http.dart' as http;

class CryptoApi {
  static const _key = ''; // Сюда ваш API ключ

  static Future<List<Currency>> getCurrencies() async {
    final url =
        "https://api.nomics.com/v1/currencies/ticker?key=$_key&per-page=100";

    final response = await http.get(Uri.parse(url));
    final parsed = jsonDecode(response.body).cast<Map<String, dynamic>>();

    return parsed;
  }
}

Затем создай модель валюты (Currency):

class Currency {
  final String id;
  final String name;
  final String currency;
  final String logoUrl;
  final double price;
  final double oneDayChange;
  final double marketCap;
  final int rank;

  Currency({
    required this.id,
    required this.name,
    required this.currency,
    required this.logoUrl,
    required this.price,
    required this.oneDayChange,
    required this.marketCap,
    required this.rank,
  });

  factory Currency.fromJson(Map<String, dynamic> json) {
    return Currency(
      id: json['id'],
      name: json['name'],
      currency: json['currency'],
      logoUrl: json['logo_url'],
      price: double.parse(json['price']),
      oneDayChange: double.parse(json['1d']['price_change_pct']),
      marketCap: double.parse(json['market_cap']),
      rank: int.parse(json['rank']),
    );
  }
}

Далее займемся файлом main.dart и отобразим наш список:

import 'package:extension_example/api/crypto_api.dart';
import 'package:extension_example/model/currency.dart';
import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        theme: ThemeData.dark(),
        themeMode: ThemeMode.dark,
        debugShowCheckedModeBanner: false,
        title: 'Crypto Tracker',
        home: const Currencies());
  }
}

class Currencies extends StatefulWidget {
  const Currencies({Key? key}) : super(key: key);

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

class _CurrenciesState extends State<Currencies> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Crypto Tracker'),
        actions: [
          IconButton(
            icon: const Icon(Icons.refresh),
            onPressed: () {
              setState(() {});
              // Обновление данных используя setstate.
              // Это не сколько чисто, сколько быстро.
            },
          ),
        ],
      ),
      body: FutureBuilder<List<Currency>>(
        future: CryptoApi.getCurrencies(),
        builder: (context, AsyncSnapshot snapshot) {
          if (!snapshot.hasData) {
            return const Center(
              child: CircularProgressIndicator(),
            );
          }
          return ListView.builder(
            itemCount: snapshot.data.length,
            itemBuilder: (context, index) {
              Currency currency = Currency.fromJson(snapshot.data[index]);

              return ListTile(
                leading: Image.network(
                  currency.logoUrl,
                ),
                title: Text("${currency.name} (${currency.currency})"),
                subtitle: Text(currency.price.toStringAsFixed(2)),
                trailing: RichText(
                  text: TextSpan(
                    children: [
                      WidgetSpan(
                        child: currency.oneDayChange >= 0
                            ? const RotatedBox(
                                quarterTurns: 1,
                                child: Icon(
                                  Icons.arrow_back_ios,
                                  color: Colors.green,
                                ),
                              )
                            : const RotatedBox(
                                quarterTurns: 3,
                                child: Icon(
                                  Icons.arrow_back_ios,
                                  color: Colors.red,
                                ),
                              ),
                      ),
                      TextSpan(
                        text: " % " +
                            (currency.oneDayChange * 100).toStringAsFixed(2),
                        style: TextStyle(
                            color: currency.oneDayChange >= 0
                                ? Colors.green
                                : Colors.red),
                      ),
                    ],
                  ),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

Еще кое-что, файл manifest.json немного отличается от другого примера. Я загружаю несколько иконок для каждого размера экрана. Не смотря на то, что все они имеют одинаковый значок, Chrome говорит, что нам нужно указать их.

А еще я добавляю host_permission. Мы используем API в нашем проекте, поэтому мы должны дать разрешение на запрос данных из API. В противном случае доступ может быть заблокирован политикой CORS:

{
    "name": "Crypto Tracker",
    "short_name": "Crypto Tracker",
    "version": "1.0.0",
    "description": "There are 100 cryptocurrencies available. You can see current prices.",
    
    "host_permissions": [
        "<all_urls>"
    ],

    "content_security_policy": {
        "extention_pages": "script-src 'self' 'unsafe-eval'; object-src 'self'"
    },
    "action": {
        "default_popup":"index.html",
        "default_icon":"/icons/bitcoin.png"
    },

    "icons": {
        "16": "icons/bitcoin.png",
        "32": "icons/bitcoin.png",
        "64": "icons/bitcoin.png",
        "128": "icons/bitcoin.png"
    },
    "manifest_version": 3
}

Теперь мы должны создать веб-приложение с помощью следующей команды:

flutter build web --web-renderer html --csp

Затем мы откроем Chrome и перейдем к chrome://extensions/. Нажмите Загрузить распакованное и выберите маршрут ../build/web/. Этот маршрут был сгенерирован на предыдущем шаге.

Мы видим, что расширение успешно добавлено:

Заключение

В этой статье мы создали простое расширение для отслеживания актуальности валют Биткойна для браузера Google Chrome, надеюсь было полезно.

Исходный код: Github.

Tags:
Hubs:
+6
Comments 6
Comments Comments 6

Articles