该案例适用于Flutter调用以太坊solidity智能合约
前言
一、需要使用哪些开发工具?
- Remix IDE
- android studio
- ubuntu
- truffle
二、操作步骤(如果出现问题看最后的总结)
1.搭建Flutter Project
首先打开Android studio开发工具,按照如下步骤进行新建项目
然后会自动生成一个Flutter项目的demo,点击如下配置文件,该文件右上角会出现 Pub get
然后可以运行该demo了。
2.集成Truffle框架
1、首先确保你的truffle已经安装,在Terminal控制台进行 truffle version
2、接着truffle init 会生成几个文件夹
3、在contract编写自己的智能合约并部署,注意修改truffle-config.js文件的配置
3.Copy代码
首先在lib文件夹下面新建如下文件夹并进行下面的dart代码复制
- main.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< dynamic> res = result;
-
return res[ 0];
-
}
-
-
Future< String> 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< String> 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<DeployedContract> 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<HomePage> createState() => _HomePageState();
-
}
-
-
class _HomePageState extends State<HomePage> {
-
-
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加端口。
3、分别代表私钥和智能合约函数名
4、这个需要修改为链的chainid
5、这个是truffle编译成的json文件所在目录
转载:https://blog.csdn.net/weixin_46085718/article/details/127427616
查看评论