image switch address
This commit is contained in:
parent
caef949f32
commit
c95771e637
|
@ -24,7 +24,8 @@ import (
|
||||||
|
|
||||||
func InitClient() {
|
func InitClient() {
|
||||||
client.Timeout = time.Second * 60
|
client.Timeout = time.Second * 60
|
||||||
switchAddress, _ = properties.LoadSwitchAddress()
|
switchAddress, _ = properties.LoadIntProperty("switchAddress", 1)
|
||||||
|
imageSwitchAddress, _ = properties.LoadIntProperty("imageSwitchAddress", 1)
|
||||||
proxy, _ := properties.LoadProxy()
|
proxy, _ := properties.LoadProxy()
|
||||||
changeProxyUrl(proxy)
|
changeProxyUrl(proxy)
|
||||||
}
|
}
|
||||||
|
@ -36,15 +37,18 @@ var dialer = &net.Dialer{
|
||||||
}
|
}
|
||||||
|
|
||||||
// SwitchAddress
|
// SwitchAddress
|
||||||
// addr = "172.67.7.24:443"
|
var switchAddresses = map[int]string{
|
||||||
// addr = "104.20.180.50:443"
|
1: "172.67.7.24:443",
|
||||||
// addr = "172.67.208.169:443"
|
2: "104.20.180.50:443",
|
||||||
var switchAddress = ""
|
3: "172.67.208.169:443",
|
||||||
var switchAddressPattern, _ = regexp.Compile("^.+picacomic\\.com:\\d+$")
|
}
|
||||||
|
|
||||||
|
var switchAddress = 1
|
||||||
|
var switchAddressPattern, _ = regexp.Compile("^.+pica" + "comic\\.com:\\d+$")
|
||||||
|
|
||||||
func switchAddressContext(ctx context.Context, network, addr string) (net.Conn, error) {
|
func switchAddressContext(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
if switchAddressPattern.MatchString(addr) && switchAddress != "" {
|
if sAddr, ok := switchAddresses[switchAddress]; ok {
|
||||||
addr = switchAddress
|
addr = sAddr
|
||||||
}
|
}
|
||||||
return dialer.DialContext(ctx, network, addr)
|
return dialer.DialContext(ctx, network, addr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
_ "golang.org/x/image/webp"
|
_ "golang.org/x/image/webp"
|
||||||
"image"
|
"image"
|
||||||
|
@ -9,20 +10,46 @@ import (
|
||||||
_ "image/jpeg"
|
_ "image/jpeg"
|
||||||
_ "image/png"
|
_ "image/png"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"pikapika/main/database/comic_center"
|
"pikapika/main/database/comic_center"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var mutexCounter = -1
|
var mutexCounter = -1
|
||||||
var busMutex *sync.Mutex
|
var busMutex *sync.Mutex
|
||||||
var subMutexes []*sync.Mutex
|
var subMutexes []*sync.Mutex
|
||||||
|
var imageHttpClient *http.Client
|
||||||
|
|
||||||
|
// imageSwitchAddress
|
||||||
|
// 图片的分流直接使用 switchAddressPattern 可以正常使用
|
||||||
|
// 通过ping发现图片的分流地址与ip一致
|
||||||
|
// 这里为了域名与官方一致改为域名分流
|
||||||
|
var imageSwitchAddresses = map[int]string{
|
||||||
|
1: "https://storage.wika" + "wika.xyz",
|
||||||
|
2: "https://s2.pica" + "comic.com",
|
||||||
|
3: "https://s3.pica" + "comic.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
var imageSwitchAddress int
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
busMutex = &sync.Mutex{}
|
busMutex = &sync.Mutex{}
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
subMutexes = append(subMutexes, &sync.Mutex{})
|
subMutexes = append(subMutexes, &sync.Mutex{})
|
||||||
}
|
}
|
||||||
|
imageHttpClient = &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
TLSHandshakeTimeout: time.Second * 10,
|
||||||
|
ExpectContinueTimeout: time.Second * 10,
|
||||||
|
ResponseHeaderTimeout: time.Second * 10,
|
||||||
|
IdleConnTimeout: time.Second * 10,
|
||||||
|
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
|
return dialer.DialContext(ctx, network, addr)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// takeMutex 下载图片获取一个锁, 这样只能同时下载5张图片
|
// takeMutex 下载图片获取一个锁, 这样只能同时下载5张图片
|
||||||
|
@ -52,6 +79,13 @@ func decodeFromFile(path string) ([]byte, image.Image, string, error) {
|
||||||
|
|
||||||
// 下载图片并decode
|
// 下载图片并decode
|
||||||
func decodeFromUrl(fileServer string, path string) ([]byte, image.Image, string, error) {
|
func decodeFromUrl(fileServer string, path string) ([]byte, image.Image, string, error) {
|
||||||
|
useClient := imageHttpClient
|
||||||
|
if imageSwitchAddress == -1 {
|
||||||
|
useClient = &client.Client
|
||||||
|
}
|
||||||
|
if server, ok := imageSwitchAddresses[imageSwitchAddress]; ok {
|
||||||
|
fileServer = server
|
||||||
|
}
|
||||||
m := takeMutex()
|
m := takeMutex()
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
@ -59,7 +93,7 @@ func decodeFromUrl(fileServer string, path string) ([]byte, image.Image, string,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, "", err
|
return nil, nil, "", err
|
||||||
}
|
}
|
||||||
response, err := client.Do(request)
|
response, err := useClient.Do(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, "", err
|
return nil, nil, "", err
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,16 +100,37 @@ func loadDownloadThreadCount() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func setSwitchAddress(nSwitchAddress string) error {
|
func setSwitchAddress(nSwitchAddress string) error {
|
||||||
err := properties.SaveSwitchAddress(nSwitchAddress)
|
num, err := strconv.Atoi(nSwitchAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
switchAddress = nSwitchAddress
|
err = properties.SaveIntProperty("switchAddress", num)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switchAddress = num
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSwitchAddress() (string, error) {
|
func getSwitchAddress() (string, error) {
|
||||||
return switchAddress, nil
|
return strconv.Itoa(switchAddress), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setImageSwitchAddress(nSwitchAddress string) error {
|
||||||
|
num, err := strconv.Atoi(nSwitchAddress)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = properties.SaveIntProperty("imageSwitchAddress", num)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switchAddress = num
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getImageSwitchAddress() (string, error) {
|
||||||
|
return strconv.Itoa(imageSwitchAddress), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setProxy(value string) error {
|
func setProxy(value string) error {
|
||||||
|
@ -534,6 +555,10 @@ func FlatInvoke(method string, params string) (string, error) {
|
||||||
return "", setSwitchAddress(params)
|
return "", setSwitchAddress(params)
|
||||||
case "getSwitchAddress":
|
case "getSwitchAddress":
|
||||||
return getSwitchAddress()
|
return getSwitchAddress()
|
||||||
|
case "setImageSwitchAddress":
|
||||||
|
return "", setImageSwitchAddress(params)
|
||||||
|
case "getImageSwitchAddress":
|
||||||
|
return getImageSwitchAddress()
|
||||||
case "setProxy":
|
case "setProxy":
|
||||||
return "", setProxy(params)
|
return "", setProxy(params)
|
||||||
case "getProxy":
|
case "getProxy":
|
||||||
|
|
|
@ -2,6 +2,7 @@ package properties
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"gorm.io/driver/sqlite"
|
"gorm.io/driver/sqlite"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"gorm.io/gorm/clause"
|
"gorm.io/gorm/clause"
|
||||||
|
@ -65,18 +66,22 @@ func LoadBoolProperty(name string, defaultValue bool) (bool, error) {
|
||||||
return strconv.ParseBool(stringValue)
|
return strconv.ParseBool(stringValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func LoadIntProperty(name string, defaultValue int) (int, error) {
|
||||||
|
str, err := LoadProperty(name, fmt.Sprintf("%d", defaultValue))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return strconv.Atoi(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SaveIntProperty(name string, value int) error {
|
||||||
|
return SaveProperty(name, strconv.Itoa(value))
|
||||||
|
}
|
||||||
|
|
||||||
func SaveBoolProperty(name string, value bool) error {
|
func SaveBoolProperty(name string, value bool) error {
|
||||||
return SaveProperty(name, strconv.FormatBool(value))
|
return SaveProperty(name, strconv.FormatBool(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
func SaveSwitchAddress(value string) error {
|
|
||||||
return SaveProperty("switch_address", value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadSwitchAddress() (string, error) {
|
|
||||||
return LoadProperty("switch_address", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveProxy(value string) error {
|
func SaveProxy(value string) error {
|
||||||
return SaveProperty("proxy", value)
|
return SaveProperty("proxy", value)
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,16 @@ class Method {
|
||||||
return await _flatInvoke("setSwitchAddress", switchAddress);
|
return await _flatInvoke("setSwitchAddress", switchAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 获取当前的图片分流
|
||||||
|
Future<String> getImageSwitchAddress() async {
|
||||||
|
return await _flatInvoke("getImageSwitchAddress", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 更换图片分流
|
||||||
|
Future<dynamic> setImageSwitchAddress(String switchAddress) async {
|
||||||
|
return await _flatInvoke("setImageSwitchAddress", switchAddress);
|
||||||
|
}
|
||||||
|
|
||||||
/// 获取代理
|
/// 获取代理
|
||||||
Future<String> getProxy() async {
|
Future<String> getProxy() async {
|
||||||
return await _flatInvoke("getProxy", "");
|
return await _flatInvoke("getProxy", "");
|
||||||
|
|
|
@ -9,10 +9,10 @@ import 'package:flutter/material.dart';
|
||||||
import '../Method.dart';
|
import '../Method.dart';
|
||||||
|
|
||||||
var _addresses = {
|
var _addresses = {
|
||||||
"不分流": "",
|
"0": "不分流",
|
||||||
"分流1": "172.67.7.24:443",
|
"1": "分流1 (推荐)",
|
||||||
"分流2": "104.20.180.50:443",
|
"2": "分流2",
|
||||||
"分流3": "72.67.208.169:443",
|
"3": "分流3",
|
||||||
};
|
};
|
||||||
|
|
||||||
late String _currentAddress;
|
late String _currentAddress;
|
||||||
|
@ -21,16 +21,11 @@ Future<void> initAddress() async {
|
||||||
_currentAddress = await method.getSwitchAddress();
|
_currentAddress = await method.getSwitchAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
String currentAddressName() {
|
String _currentAddressName() {
|
||||||
for (var value in _addresses.entries) {
|
return _addresses[_currentAddress] ?? "";
|
||||||
if (value.value == _currentAddress) {
|
|
||||||
return value.key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> chooseAddress(BuildContext context) async {
|
Future<void> _chooseAddress(BuildContext context) async {
|
||||||
String? choose = await showDialog<String>(
|
String? choose = await showDialog<String>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
|
@ -39,9 +34,9 @@ Future<void> chooseAddress(BuildContext context) async {
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
..._addresses.entries.map(
|
..._addresses.entries.map(
|
||||||
(e) => SimpleDialogOption(
|
(e) => SimpleDialogOption(
|
||||||
child: Text(e.key),
|
child: Text(e.value),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop(e.value);
|
Navigator.of(context).pop(e.key);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -54,3 +49,18 @@ Future<void> chooseAddress(BuildContext context) async {
|
||||||
_currentAddress = choose;
|
_currentAddress = choose;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget switchAddressSetting() {
|
||||||
|
return StatefulBuilder(
|
||||||
|
builder: (BuildContext context, void Function(void Function()) setState) {
|
||||||
|
return ListTile(
|
||||||
|
title: Text("分流"),
|
||||||
|
subtitle: Text(_currentAddressName()),
|
||||||
|
onTap: () async {
|
||||||
|
await _chooseAddress(context);
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../Method.dart';
|
||||||
|
|
||||||
|
var _imageAddresses = {
|
||||||
|
"-1": "跟随api分流",
|
||||||
|
"0": "不分流",
|
||||||
|
"1": "分流1 (推荐)",
|
||||||
|
"2": "分流2",
|
||||||
|
"3": "分流3",
|
||||||
|
};
|
||||||
|
|
||||||
|
late String _currentImageAddress;
|
||||||
|
|
||||||
|
Future<void> initImageAddress() async {
|
||||||
|
_currentImageAddress = await method.getImageSwitchAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
String _currentImageAddressName() {
|
||||||
|
return _imageAddresses[_currentImageAddress] ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _chooseImageAddress(BuildContext context) async {
|
||||||
|
String? choose = await showDialog<String>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return SimpleDialog(
|
||||||
|
title: Text('选择图片分流'),
|
||||||
|
children: <Widget>[
|
||||||
|
..._imageAddresses.entries.map(
|
||||||
|
(e) => SimpleDialogOption(
|
||||||
|
child: Text(e.value),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop(e.key);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (choose != null) {
|
||||||
|
await method.setImageSwitchAddress(choose);
|
||||||
|
_currentImageAddress = choose;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget imageSwitchAddressSetting() {
|
||||||
|
return StatefulBuilder(
|
||||||
|
builder: (BuildContext context, void Function(void Function()) setState) {
|
||||||
|
return ListTile(
|
||||||
|
title: Text("图片"),
|
||||||
|
subtitle: Text(_currentImageAddressName()),
|
||||||
|
onTap: () async {
|
||||||
|
await _chooseImageAddress(context);
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
|
@ -28,3 +28,18 @@ Future<dynamic> inputProxy(BuildContext context) async {
|
||||||
_currentProxy = input;
|
_currentProxy = input;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget proxySetting() {
|
||||||
|
return StatefulBuilder(
|
||||||
|
builder: (BuildContext context, void Function(void Function()) setState) {
|
||||||
|
return ListTile(
|
||||||
|
title: Text("代理服务器"),
|
||||||
|
subtitle: Text(currentProxyName()),
|
||||||
|
onTap: () async {
|
||||||
|
await inputProxy(context);
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -6,17 +6,17 @@ import 'package:pikapika/basic/Navigatior.dart';
|
||||||
import 'basic/config/Themes.dart';
|
import 'basic/config/Themes.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(PikapiApp());
|
runApp(PikapikaApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
class PikapiApp extends StatefulWidget {
|
class PikapikaApp extends StatefulWidget {
|
||||||
const PikapiApp({Key? key}) : super(key: key);
|
const PikapikaApp({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<StatefulWidget> createState() => _PikapiAppState();
|
State<StatefulWidget> createState() => _PikapikaAppState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PikapiAppState extends State<PikapiApp> {
|
class _PikapikaAppState extends State<PikapikaApp> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
themeEvent.subscribe(_onChangeTheme);
|
themeEvent.subscribe(_onChangeTheme);
|
||||||
|
|
|
@ -11,6 +11,7 @@ import 'package:pikapika/basic/config/DownloadAndExportPath.dart';
|
||||||
import 'package:pikapika/basic/config/DownloadThreadCount.dart';
|
import 'package:pikapika/basic/config/DownloadThreadCount.dart';
|
||||||
import 'package:pikapika/basic/config/FullScreenAction.dart';
|
import 'package:pikapika/basic/config/FullScreenAction.dart';
|
||||||
import 'package:pikapika/basic/config/FullScreenUI.dart';
|
import 'package:pikapika/basic/config/FullScreenUI.dart';
|
||||||
|
import 'package:pikapika/basic/config/ImageAddress.dart';
|
||||||
import 'package:pikapika/basic/config/KeyboardController.dart';
|
import 'package:pikapika/basic/config/KeyboardController.dart';
|
||||||
import 'package:pikapika/basic/config/NoAnimation.dart';
|
import 'package:pikapika/basic/config/NoAnimation.dart';
|
||||||
import 'package:pikapika/basic/config/PagerAction.dart';
|
import 'package:pikapika/basic/config/PagerAction.dart';
|
||||||
|
@ -49,6 +50,7 @@ class _InitScreenState extends State<InitScreen> {
|
||||||
await initPlatform(); // 必须第一个初始化, 加载设备信息
|
await initPlatform(); // 必须第一个初始化, 加载设备信息
|
||||||
await initAutoClean();
|
await initAutoClean();
|
||||||
await initAddress();
|
await initAddress();
|
||||||
|
await initImageAddress();
|
||||||
await initProxy();
|
await initProxy();
|
||||||
await initQuality();
|
await initQuality();
|
||||||
await initFont();
|
await initFont();
|
||||||
|
|
|
@ -220,7 +220,7 @@ class _RemoteImageState extends State<RemoteImage> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
_mock = widget.fileServer == "" || widget.fileServer.contains(".xyz/");
|
_mock = widget.fileServer == "";
|
||||||
if (!_mock) {
|
if (!_mock) {
|
||||||
_future = method
|
_future = method
|
||||||
.remoteImageData(widget.fileServer, widget.path)
|
.remoteImageData(widget.fileServer, widget.path)
|
||||||
|
|
|
@ -1,35 +1,18 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:pikapika/basic/config/Address.dart';
|
import 'package:pikapika/basic/config/Address.dart';
|
||||||
|
import 'package:pikapika/basic/config/ImageAddress.dart';
|
||||||
import 'package:pikapika/basic/config/Proxy.dart';
|
import 'package:pikapika/basic/config/Proxy.dart';
|
||||||
|
|
||||||
// 网络设置
|
// 网络设置
|
||||||
class NetworkSetting extends StatefulWidget {
|
class NetworkSetting extends StatelessWidget {
|
||||||
@override
|
|
||||||
State<StatefulWidget> createState() => _NetworkSettingState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _NetworkSettingState extends State<NetworkSetting> {
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
switchAddressSetting(),
|
||||||
title: Text("分流"),
|
imageSwitchAddressSetting(),
|
||||||
subtitle: Text(currentAddressName()),
|
proxySetting(),
|
||||||
onTap: () async {
|
|
||||||
await chooseAddress(context);
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text("代理服务器"),
|
|
||||||
subtitle: Text(currentProxyName()),
|
|
||||||
onTap: () async {
|
|
||||||
await inputProxy(context);
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -13,7 +13,7 @@ import 'package:pikapika/main.dart';
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||||
// Build our app and trigger a frame.
|
// Build our app and trigger a frame.
|
||||||
await tester.pumpWidget(PikapiApp());
|
await tester.pumpWidget(PikapikaApp());
|
||||||
|
|
||||||
// Verify that our counter starts at 0.
|
// Verify that our counter starts at 0.
|
||||||
expect(find.text('0'), findsOneWidget);
|
expect(find.text('0'), findsOneWidget);
|
||||||
|
|
Loading…
Reference in New Issue