Flutter调用以太坊区块链智能合约 (私链)

该案例适用于Flutter调用以太坊solidity智能合约

文章目录

  • 前言
  • 一、需要使用的工具?
  • 二、操作步骤
    • 1.搭建Flutter Project
    • 2.编写Flutter相关代码
  • 总结


前言


一、需要使用哪些开发工具?

  1. Remix IDE
  2. android studio
  3. ubuntu
  4. truffle

二、操作步骤(如果出现问题看最后的总结)

1.搭建Flutter Project

 首先打开Android studio开发工具,按照如下步骤进行新建项目

Flutter调用以太坊区块链智能合约 (私链)_第1张图片

 Flutter调用以太坊区块链智能合约 (私链)_第2张图片

 然后会自动生成一个Flutter项目的demo,点击如下配置文件,该文件右上角会出现 Pub get

Flutter调用以太坊区块链智能合约 (私链)_第3张图片

 然后可以运行该demo了。


2.集成Truffle框架

   1、首先确保你的truffle已经安装,在Terminal控制台进行 truffle version

Flutter调用以太坊区块链智能合约 (私链)_第4张图片

2、接着truffle init 会生成几个文件夹

Flutter调用以太坊区块链智能合约 (私链)_第5张图片

 3、在contract编写自己的智能合约并部署,注意修改truffle-config.js文件的配置

Flutter调用以太坊区块链智能合约 (私链)_第6张图片


 3.Copy代码

   首先在lib文件夹下面新建如下文件夹并进行下面的dart代码复制

  1. 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(),
        );
      }
    }
    
    
    
  2.  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;
      }
    }
  3.  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();
                    },
                  ),
                ],
              );
            });
      }
    }
    
  4. 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
                ),
              ),
            ),
          ),
        );
      }
    }
    
  5. 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
    

  6. 新建一个 .env文件

    METAMASK_WALLET_ADDRESS = 0x94B6D5dC903c96299C16bdBe45652A61a2334424
    METAMASK_PRIVATE_KEY = ee4923c9c3154ccc71697c048ad079c600e09b8a730f1c80b8931ca0c519dc3f
    CONTRACT_ADDRESS = 0x0585c0f265D6073d7D46ed339Eaf2b8035982655
    METAMASK_WALLET_ADDRESS是钱包地址
    METAMASK_PRIVATE_KEY是账户私钥
    CONTRACT_ADDRESS是智能合约地址(在Remix中可以获取,truffle框架进行编译也可以进行获取)
  7. Flutter调用以太坊区块链智能合约 (私链)_第7张图片Flutter调用以太坊区块链智能合约 (私链)_第8张图片Flutter调用以太坊区块链智能合约 (私链)_第9张图片


 智能合约

// 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;
    }

  }

 运行截图:

Flutter调用以太坊区块链智能合约 (私链)_第10张图片

总结

特别注意的地方:

1、新建一个 .env文件(有一个点),在flutter项目目录下。里面的配置需要按照自己私链的配置及metamask连上私链配置

2、在models文件夹下的etherum_utils.dart中,这个需要修改为自己私链的ip加端口。

 3、分别代表私钥和智能合约函数名Flutter调用以太坊区块链智能合约 (私链)_第11张图片

 4、这个需要修改为链的chainid

Flutter调用以太坊区块链智能合约 (私链)_第12张图片

 5、这个是truffle编译成的json文件所在目录

Flutter调用以太坊区块链智能合约 (私链)_第13张图片

 

你可能感兴趣的:(Flutter,区块链,以太坊,区块链,智能合约)