Android

一个Android菜鸟入门Flutter 笔记(二)

2020-04-16  本文已影响0人  潇风寒月

1. 网络编程与JSON解析


get() async {
  //创建网络调用示例,设置通用请求行为(超时时间)
  var httpClient = HttpClient();
  httpClient.idleTimeout = Duration(seconds: 5);
  
  //构造URI,设置user-agent为"Custom-UA"
  var uri = Uri.parse("https://flutter.dev");
  var request = await httpClient.getUrl(uri);
  request.headers.add("user-agent", "Custom-UA");
  
  //发起请求,等待响应
  var response = await request.close();
  
  //收到响应,打印结果
  if (response.statusCode == HttpStatus.ok) {
    print(await response.transform(utf8.decoder).join());
  } else {
    print('Error: \nHttp status ${response.statusCode}');
  }
}

httpGet() async {
  //创建网络调用示例
  var client = http.Client();

  //构造URI
  var uri = Uri.parse("https://flutter.dev");
  
  //设置user-agent为"Custom-UA",随后立即发出请求
  http.Response response = await client.get(uri, headers : {"user-agent" : "Custom-UA"});

  //打印请求结果
  if(response.statusCode == HttpStatus.ok) {
    print(response.body);
  } else {
    print("Error: ${response.statusCode}");
  }
}

void getRequest() async {
  //创建网络调用示例
  Dio dio = new Dio();
  
  //设置URI及请求user-agent后发起请求
  var response = await dio.get("https://flutter.dev", options:Options(headers: {"user-agent" : "Custom-UA"}));
  
 //打印请求结果
  if(response.statusCode == HttpStatus.ok) {
    print(response.data.toString());
  } else {
    print("Error: ${response.statusCode}");
  }
}

//下载-------------

//使用FormData表单构建待上传文件
FormData formData = FormData.from({
  "file1": UploadFileInfo(File("./file1.txt"), "file1.txt"),
  "file2": UploadFileInfo(File("./file2.txt"), "file1.txt"),

});
//通过post方法发送至服务端
var responseY = await dio.post("https://xxx.com/upload", data: formData);
print(responseY.toString());

//使用download方法下载文件
dio.download("https://xxx.com/file1", "xx1.zip");

//增加下载进度回调函数
dio.download("https://xxx.com/file1", "xx2.zip", onReceiveProgress: (count, total) {
  //do something      
});


//并行请求--------------

//同时发起两个并行请求
List<Response> responseX= await Future.wait([dio.get("https://flutter.dev"),dio.get("https://pub.dev/packages/dio")]);

//打印请求1响应结果
print("Response1: ${responseX[0].toString()}");
//打印请求2响应结果
print("Response2: ${responseX[1].toString()}");

//拦截器-----------------

//增加拦截器
dio.interceptors.add(InterceptorsWrapper(
    onRequest: (RequestOptions options){
      //为每个请求头都增加user-agent
      options.headers["user-agent"] = "Custom-UA";
      //检查是否有token,没有则直接报错
      if(options.headers['token'] == null) {
        return dio.reject("Error:请先登录");
      } 
      //检查缓存是否有数据
      if(options.uri == Uri.parse('http://xxx.com/file1')) {
        return dio.resolve("返回缓存数据");
      }
      //放行请求
      return options;
    }
));

//增加try catch,防止请求报错
try {
  var response = await dio.get("https://xxx.com/xxx.zip");
  print(response.data.toString());
}catch(e) {
  print(e);
}

2.JSON解析

import 'dart:convert';

String jsonString = '''
{
  "id":"123",
  "name":"张三",
  "score" : 95,
  "teacher": { "name": "李四", "age" : 40 }
}
''';

//json解析

//所谓手动解析,是指使用 dart:convert 库中内置的 JSON 解码器,将 JSON 字符串解析成自定义对象的过程。

class Teacher {
  String name;
  int age;

  Teacher({this.name, this.age});

  factory Teacher.fromJson(Map<String, dynamic> parsedJson) {
    return Teacher(name: parsedJson['name'], age: parsedJson['age']);
  }

  @override
  String toString() {
    return 'Teacher{name: $name, age: $age}';
  }
}

class Student {
  String id;
  String name;
  int score;
  Teacher teacher;

  Student({this.id, this.name, this.score, this.teacher});

  //从Map中取
  factory Student.fromJson(Map<String, dynamic> parsedJson) {
    return Student(
        id: parsedJson['id'],
        name: parsedJson['name'],
        score: parsedJson['score'],
        teacher: Teacher.fromJson(parsedJson['teacher']));
  }

  @override
  String toString() {
    return 'Student{id: $id, name: $name, score: $score, teacher: $teacher}';
  }
}


void main() {

  final jsonResponse = json.decode(jsonString);//将字符串解码成Map对象
  Student student = Student.fromJson(jsonResponse);//手动解析
  print(student.teacher.name);
}

3. 数据持久化

3.1 文件

需要引入: path_provider: ^1.6.4


//创建文件目录
Future<File> get _localFile async {
  final directory = await getApplicationDocumentsDirectory();
  final path = directory.path;
  return File('$path/content.txt');
}
//将字符串写入文件
Future<File> writeContent(String content) async {
  final file = await _localFile;
  return file.writeAsString(content);
}
//从文件读出字符串
Future<String> readContent() async {
  try {
    final file = await _localFile;
    String contents = await file.readAsString();
    return contents;
  } catch (e) {
    return "";
  }
}

3.2 SharedPreferences

需要引入: shared_preferences: ^0.5.6+2


//读取SharedPreferences中key为counter的值
Future<int>_loadCounter() async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  int  counter = (prefs.getInt('counter') ?? 0);
  return counter;
}

//递增写入SharedPreferences中key为counter的值
Future<void>_incrementCounter() async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
    int counter = (prefs.getInt('counter') ?? 0) + 1;
    prefs.setInt('counter', counter);
}

3.3 数据库

需要引入: sqflite: ^1.2.1

dbDemo() async {
    final Future<Database> database = openDatabase(
      //join是拼接路径分隔符
      join(await getDatabasesPath(), 'student_database.db'),
      onCreate: (db, version) => db.execute(
          "CREATE TABLE students(id TEXT PRIMARY KEY,name TEXT,score INTEGER)"),
      onUpgrade: (db, oldVersion, newVersion) {
        //dosth for 升级
      },
      version: 1,
    );

    Future<void> insertStudent(Student std) async {
      final Database db = await database;
      await db.insert(
        'students',
        std.toJson(),
        //插入冲突策略,新的替换旧的
        conflictAlgorithm: ConflictAlgorithm.replace,
      );
    }

    //插入3个
    await insertStudent(student1);
    await insertStudent(student2);
    await insertStudent(student3);

    Future<List<Student>> students() async {
      final Database db = await database;
      final List<Map<String, dynamic>> maps = await db.query('students');
      return List.generate(maps.length, (i) => Student.fromJson(maps[i]));
    }

    ////读取出数据库中插入的Student对象集合
    students().then((list) => list.forEach((s) => print(s.name)));
    //释放数据库资源
    final Database db = await database;
    db.close();
  }

4. Flutter调原生

class _MyHomePageState extends State<MyHomePage> {
  //声明MethodChannel
  static const platform = MethodChannel('com.xfhy.basic_ui/util');


  handleButtonClick() async {
    bool result;
    //捕获  万一失败了呢
    try {
      //异步等待,可能很耗时  等待结果
      result = await platform.invokeMethod('isEmpty', "have data");
    } catch (e) {
      result = false;
    }
    print('result : $result');
  }

}

//Android代码
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.GeneratedPluginRegistrant

class MainActivity : FlutterActivity() {

    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        GeneratedPluginRegistrant.registerWith(flutterEngine)

        //参考: https://flutter.dev/docs/development/platform-integration/platform-channels

        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "com.xfhy.basic_ui/util").setMethodCallHandler { call, result ->
            //判断方法名是否支持
            if (call.method == "isEmpty") {
                val arguments = call.arguments
                result.success(StringUtil.isEmpty(arguments as? String))
                print("success")
            } else {
                //方法名暂不支持
                result.notImplemented()
                print("fail")
            }
        }
    }
}


5. Flutter中复用原生控件

6. Android项目中嵌入Flutter

官网地址: https://flutter.dev/docs/development/add-to-app

7. 混合开发导航栈

8. 状态管理(跨组件传递数据,Provider)


//定义需要共享的数据模型,通过混入ChangeNotifier管理听众
class CounterModel with ChangeNotifier {
  int _count = 0;
  //读方法
  int get counter => _count; 
  //写方法
  void increment() {
    _count++;
    notifyListeners();//通知听众刷新
  }
}
尽量把数据放到更高的层级

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //通过Provider组件封装数据资源
    //因Provider是InheritedWidget的语法糖,所以它是一个Widget
    //ChangeNotifierProvider只能搞一个
    //MultiProvider可以搞多个
    return MultiProvider(
      providers: [
        //注入字体大小  下个界面读出来
        Provider.value(value: 30.0),
        //注入计数器实例
        ChangeNotifierProvider.value(value: CounterModel())
      ],
      child: MaterialApp(
        home: FirstPage(),
      ),
    );
  }
}
//示例: 读数据
class FirstPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //取出资源  类型是CounterModel
    //获取计时器实例
    final _counter = Provider.of<CounterModel>(context);
    //获取字体大小
    final textSize = Provider.of<double>(context);

    /*
    *
//使用Consumer2获取两个数据资源
Consumer2<CounterModel,double>(
  //builder函数以参数的形式提供了数据资源
  builder: (context, CounterModel counter, double textSize, _) => Text(
      'Value: ${counter.counter}',
      style: TextStyle(fontSize: textSize))
)
* 我们最多可以使用到 Consumer6,即共享 6 个数据资源。
    * */

    return Scaffold(
      body: Center(
        child: Text(
          'Counter: ${_counter.counter}',
          style: TextStyle(fontSize: textSize),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Text('Go'),
        onPressed: () => Navigator.of(context)
            .push(MaterialPageRoute(builder: (context) => SecondPage())),
      ),
    );
  }
}

//示例: 读和写数据
//使用Consumer 可以精准刷新发生变化的Widget
class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //取出数据
    //final _counter = Provider.of<CounterModel>(context);
    return Scaffold(
      //使用Consumer来封装counter的读取
      body: Consumer(
        //builder函数可以直接获取到counter参数
        //Consumer 中的 builder 实际上就是真正刷新 UI 的函数,它接收 3 个参数,即 context、model 和 child
        builder: (context, CounterModel counter, _) => Center(
          child: Text('Value: ${counter.counter}'),
        ),
      ),
      floatingActionButton: Consumer<CounterModel>(
        builder: (context, CounterModel counter, child) => FloatingActionButton(
          onPressed: counter.increment,
          child: child,
        ),
        child: Icon(Icons.add),
      ),
    );
  }
}

9. 适配不同分辨率的手机屏幕

10. 编译模式

if (kReleaseMode) {
  //正式环境
  text = "release";
} else {
  //测试环境  debug
  text = "debug";
}

配置一些app的通用配置

///配置抽象
class AppConfig extends InheritedWidget {
  //主页标题
  final String appName;

  //接口域名
  final String apiBaseUrl;

  AppConfig(
      {@required this.appName,
      @required this.apiBaseUrl,
      @required Widget child})
      : super(child: child);

  //方便其子Widget在Widget树中找到它
  static AppConfig of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(AppConfig);
  }

  //判断是否需要子Widget更新.由于是应用入口,无需更新
  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    return false;
  }
}

///为不同的环境创建不同的应用入口

//main_dev.dart    这个是正式环境的入口
void main() {
  var configuredApp = AppConfig(
    appName: 'dev', //主页标题
    apiBaseUrl: 'http://dev.example.com/', //接口域名
    child: MyApp(),
  );
  runApp(configuredApp);
}

//main.dart   这个是测试环境的入口
/*void main(){
  var configuredApp = AppConfig(){
    appName: 'example',//主页标题
   apiBaseUrl: 'http://api.example.com/',//接口域名
   child: MyApp(),
  }
  runApp(configuredApp);
}*/

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var config = AppConfig.of(context);
    return MaterialApp(
      title: config.appName,
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var config = AppConfig.of(context);
    return Scaffold(
      appBar: AppBar(
        title: Text(config.appName),
      ),
      body: Center(
        child: Text(config.apiBaseUrl),
      ),
    );
  }
}



//运行开发环境应用程序
//flutter run -t lib/main_dev.dart

//运行生产环境应用程序
//flutter run -t lib/main.dart

/*
*
//打包开发环境应用程序
flutter build apk -t lib/main_dev.dart
flutter build ios -t lib/main_dev.dart

//打包生产环境应用程序
flutter build apk -t lib/main.dart
flutter build ios -t lib/main.dart
* */

11. Hot Reload

12. 关于调试

  // 正式环境 将debugPrint指定为空的执行体, 所以它什么也不做
  debugPrint = (String message, {int wrapWidth}) {};
  debugPrint('test');

  //开发环境就需要打印出日志
  debugPrint = (String message, {int wrapWidth}) =>
      debugPrintSynchronously(message, wrapWidth: wrapWidth);
void main() {
  //Debug Painting 界面调试工具
  //有点像原生的显示布局边界
  debugPaintSizeEnabled = true;
  runApp(MyApp());
}

13. 常用命令行

阶段 子任务 命令
工程初始化 App工程 flutter create --template=app hello
工程初始化 Dart包工程 flutter create --template=package hello
工程初始化 插件工程 flutter create --template=plugin hello
构建 Debug构建 flutter build apk --debug </p> flutter build ios --debug
构建 Release构建 flutter build apk --release </p> flutter build ios --release
构建 Profile构建 flutter build apk --profile </p> flutter build ios --profile
集成原生工程 独立App打包 flutter build apk --release </p> flutter build ios --release
集成原生工程 Pod/AAR打包 flutter build apk --release </p> flutter build ios --release
上一篇 下一篇

猜你喜欢

热点阅读