该案例适用于Flutter调用以太坊solidity智能合约
首先打开Android studio开发工具,按照如下步骤进行新建项目
然后会自动生成一个Flutter项目的demo,点击如下配置文件,该文件右上角会出现 Pub get
然后可以运行该demo了。
1、首先确保你的truffle已经安装,在Terminal控制台进行 truffle version
2、接着truffle init 会生成几个文件夹
3、在contract编写自己的智能合约并部署,注意修改truffle-config.js文件的配置
首先在lib文件夹下面新建如下文件夹并进行下面的dart代码复制
import 'package:flutter/material.dart';
import 'package:flutter_basic_dapp/pages/home_page.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
Future main() async {
await dotenv.load(fileName: ".env");
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
ethereum_utils.dart
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:http/http.dart';
import 'package:web3dart/web3dart.dart';
var apiUrl = "http://192.168.1.161:8545"; //Replace with your API
var httpClient = Client();
var web3client = Web3Client(apiUrl, httpClient);
class EthereumUtils {
final contractAddress = dotenv.env['CONTRACT_ADDRESS'];
Future getBalance() async {
final contract = await getDeployedContract();
final etherFunction = contract.function("getBalance");
final result = await web3client.call(contract: contract, function: etherFunction, params: []);
List res = result;
return res[0];
}
Future sendBalance(int amount) async {
var bigAmount = BigInt.from(amount);
EthPrivateKey privateKeyCred = EthPrivateKey.fromHex(dotenv.env['METAMASK_PRIVATE_KEY']!);
DeployedContract contract = await getDeployedContract();
final etherFunction = contract.function("sendBalance");
final result = await web3client.sendTransaction(
privateKeyCred,
Transaction.callContract(
contract: contract,
function: etherFunction,
parameters: [bigAmount],
maxGas: 100000,
),chainId: 20220824,
fetchChainIdFromNetworkId: false);
return result;
}
Future withDrawBalance(int amount) async {
var bigAmount = BigInt.from(amount);
EthPrivateKey privateKeyCred = EthPrivateKey.fromHex(dotenv.env['METAMASK_PRIVATE_KEY']!);
DeployedContract contract = await getDeployedContract();
final etherFunction = contract.function("withDrawBalance");
final result = await web3client.sendTransaction(
privateKeyCred,
Transaction.callContract(
contract: contract,
function: etherFunction,
parameters: [bigAmount],
maxGas: 100000,
),chainId: 20220824,
fetchChainIdFromNetworkId: false);
return result;
}
Future getDeployedContract() async {
String abiStringFile = await rootBundle.loadString("assets/artifacts/BasicDapp.json");
var jsonAbi = jsonDecode(abiStringFile);
final contract = DeployedContract(ContractAbi.fromJson(jsonEncode(jsonAbi["abi"]), "BasicDapp"), EthereumAddress.fromHex(contractAddress!));
return contract;
}
}
home_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_basic_dapp/models/ethereum_utils.dart';
import 'package:flutter_basic_dapp/widgets/button_container_widget.dart';
import 'package:syncfusion_flutter_sliders/sliders.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State createState() => _HomePageState();
}
class _HomePageState extends State {
EthereumUtils ethUtils = EthereumUtils();
double? _value = 0.0;
var _data;
@override
void initState() {
ethUtils.getBalance().then((value) {
_data = value;
setState(() {
});
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.deepPurple.withOpacity(.9),
appBar: AppBar(
title: Text(" "),
),
body: Container(
margin: EdgeInsets.symmetric(horizontal: 20, vertical: 30),
// child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 0.13,
decoration: BoxDecoration(
color: Colors.deepPurple..withOpacity(.4),
borderRadius: BorderRadius.circular(10),
),
child: Center(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
child: Column(
children: [
Text("Current Balance", style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold, color: Colors.white),),
SizedBox(height: 12,),
_data == null ? CircularProgressIndicator() : Text("${_data}", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30, color: Colors.white),)
],
),
),
),
),
SizedBox(height: 40,),
SfSlider(
value: _value,
onChanged: (value) {
setState(() {
_value = value;
});
} ,
interval: 1,
activeColor: Colors.white,
enableTooltip: true,
stepSize: 1.0,
showLabels: true,
min: 0.0,
max: 10.0,
),
SizedBox(height: 40,),
CustomContainerButtonWidget(title: "Get Balance", color: Colors.green, onTap: () {
ethUtils.getBalance().then((value) {
_data = value;
setState(() {
});
});
}),
SizedBox(height: 40,),
CustomContainerButtonWidget(title: "Send Balance", color: Colors.deepPurpleAccent,
onTap: () async {
await ethUtils.sendBalance(_value!.toInt());
if (_value == 0) {
incorrectValueDialogBox(context);
} else {
sendDialogBox(context);
}
}),
SizedBox(height: 40,),
CustomContainerButtonWidget(title: "WithDraw", color: Colors.deepOrange,
onTap: () async {
await ethUtils.withDrawBalance(_value!.toInt());
if (_value == 0) {
incorrectValueDialogBox(context);
} else {
withDrawDialogBox(context);
}
}),
],
),
// ),
),
);
}
incorrectValueDialogBox(BuildContext context) {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
'Invalid Value',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18.0,
),
),
content: const Text('Please put a value greater then 0.',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.black87,
)),
actions: [
ElevatedButton(
child: Text('OK'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
});
}
sendDialogBox(BuildContext context) {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
"Thanks for your Transaction",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20.0,
),
),
),
actions: [
ElevatedButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
});
}
withDrawDialogBox(BuildContext context) {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
"Thanks for your Withdrawal",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20.0,
),
),
),
actions: [
ElevatedButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
});
}
}
button_container_widget.dart
import 'package:flutter/material.dart';
class CustomContainerButtonWidget extends StatelessWidget {
final String title;
final Color color;
final VoidCallback onTap;
final int? value;
const CustomContainerButtonWidget({Key? key, required this.title, required this.color, required this.onTap, this.value}) : super(key: key);
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Container(
height: 60,
width: MediaQuery.of(context).size.width * 0.6,
decoration: BoxDecoration(
color: color,
),
child: Center(
child: Text(
title, textAlign: TextAlign.left,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white
),
),
),
),
);
}
}
pubspec.yaml
name: flutter_basic_dapp
description: A new Flutter project.
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1
environment:
sdk: ">=2.16.1 <3.0.0"
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
flutter_dotenv: ^5.0.2
http: ^0.13.4
web3dart: ^2.3.5
syncfusion_flutter_sliders: ^19.4.56
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
dev_dependencies:
flutter_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^1.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
assets:
- .env
- assets/artifacts/.
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages
新建一个 .env文件
METAMASK_WALLET_ADDRESS = 0x94B6D5dC903c96299C16bdBe45652A61a2334424
METAMASK_PRIVATE_KEY = ee4923c9c3154ccc71697c048ad079c600e09b8a730f1c80b8931ca0c519dc3f
CONTRACT_ADDRESS = 0x0585c0f265D6073d7D46ed339Eaf2b8035982655
METAMASK_WALLET_ADDRESS是钱包地址 METAMASK_PRIVATE_KEY是账户私钥 CONTRACT_ADDRESS是智能合约地址(在Remix中可以获取,truffle框架进行编译也可以进行获取)
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 < 0.9.0;
contract BasicDapp {
uint balance;
constructor() {
balance = 0;
}
function sendBalance(uint amount) public {
balance += amount;
}
function withDrawBalance(uint amount) public {
require(balance > amount ,"Not Enough Balance");
balance -= amount;
}
function getBalance() public view returns (uint){
return balance;
}
}
运行截图:
特别注意的地方:
1、新建一个 .env文件(有一个点),在flutter项目目录下。里面的配置需要按照自己私链的配置及metamask连上私链配置
2、在models文件夹下的etherum_utils.dart中,这个需要修改为自己私链的ip加端口。
4、这个需要修改为链的chainid
5、这个是truffle编译成的json文件所在目录