Introduction and use of Unity UGUI's PhysicsRaycaster (physical raycaster, scalable line detection) component

1. Navigation and Routing

In Flutter, navigation and routing are key concepts for building multi-page applications. Navigation is the process of switching from one page (or routing) to another. Each page corresponds to a Widget. In Flutter, page switching is managed by Navigator.

1.1. Basic Navigation

In Flutter, MaterialApp is used to manage the navigation stack. When a new MaterialApp is created, it automatically creates a routing stack and places a Navigator on top of the stack.

To navigate to a new page, you can use the Navigator.push() method:

Navigator.push(context, MaterialPageRoute(builder: (context) => SecondPage()));

To return to the previous page, use the Navigator.pop() method:

Navigator.pop(context);

1.2. Named routing

Flutter also supports named routing, which allows you to use more readable names to navigate within your app. To use named routing, first define the routing table in MaterialApp:

MaterialApp(
  routes: {
    '/': (context) => HomePage(),
    '/second': (context) => SecondPage(),
  },
)

You can then use named routes for navigation:

Navigator.pushNamed(context, '/second');

1.3. Route with parameters

Sometimes you need to pass parameters to the new page. In Flutter, you can use ModalRoute.of() to get the parameters in the route:

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final args = ModalRoute.of(context).settings.arguments as Map<String, dynamic>;
    // 使用参数
    return Scaffold(...);
  }
}

To pass parameters, you can pass them in during navigation:

Navigator.pushNamed(context, '/second', arguments: {'name': 'John', 'age': 30});

1.4. Routing transition animation

Flutter provides rich routing transition animation effects, such as gradient, zoom, translation, etc. You can set PageTransitionsBuilder in MaterialPageRoute to customize transition animation:

MaterialApp(
  routes: {
    '/': (context) => HomePage(),
    '/second': (context) => SecondPage(),
  },
  theme: ThemeData(
    pageTransitionsTheme: PageTransitionsTheme(
      builders: {
        TargetPlatform.android: CupertinoPageTransitionsBuilder(), // 使用iOS样式的转场动画
      },
    ),
  ),
)

This is just a basic introduction to navigation and routing. Flutter provides more navigation and routing functions, such as Hero animation, route interception, etc. You can learn more about navigation and routing by reading the official documentation and sample code.

2. Status management

In Flutter, state management is an important aspect of handling shared data and state changes between different pages in the application. There are many state management solutions in Flutter, among which the more popular ones are Provider, Riverpod and Bloc.

2.1. Provider

Provider is a lightweight, easy-to-use state management library. It allows you to share data in the Widget tree and obtain the data through Consumer or Provider.of.

First, create a ChangeNotifierProvider in the root Widget of the application and place the data model to be shared in it:

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => CounterModel(),
      child: MyApp(),
    ),
  );
}

Then, in the Widget that needs to use data, use Consumer to subscribe to data changes:

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = context.watch<CounterModel>();
    return Text('Count: ${counter.count}');
  }
}

When the data in CounterModel changes, MyWidget will automatically update.

2.2. Riverpod

Riverpod is a new state management library that is an improved version of Provider. Riverpod provides better performance and simpler API.

To use Riverpod, first create a Provider:

final counterProvider = Provider<int>((ref) => 0);

Then, use ProviderListener to subscribe to data changes:

class MyWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, ScopedReader watch) {
    final counter = watch(counterProvider);
    return Text('Count: $counter');
  }
}

2.3. Bloc

Bloc is another commonly used state management library that uses one-way data flow to manage state. Bloc separates state and operations, making code easier to maintain and test.

First, create a Bloc:

enum CounterEvent { increment, decrement }

class CounterBloc extends Bloc<CounterEvent, int> {
  @override
  int get initialState => 0;

  @override
  Stream<int> mapEventToState(CounterEvent event) async* {
    switch (event) {
      case CounterEvent.increment:
        yield state + 1;
        break;
      case CounterEvent.decrement:
        yield state - 1;
        break;
    }
  }
}

Then, in the Widget that needs to use Bloc, use BlocBuilder to subscribe to state changes:

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocBuilder<CounterBloc, int>(
      builder: (context, state) {
        return Text('Count: $state');
      },
    );
  }
}

This is just a basic introduction to state management. Provider, Riverpod and Bloc all provide more functions and advanced usage. Learning state management in depth requires a certain amount of time and practice. You can learn more tips and best practices by reading official documentation and sample code.

3. Asynchronous processing

In Flutter, asynchronous processing is very common, such as getting data from the network, reading local files, etc. Flutter provides Future and Stream to handle asynchronous operations.

3.1. Future

Future represents an asynchronous operation that may complete or fail. To perform an asynchronous task, you can use the async and await keywords:

Future<String> fetchData() async {
  // 模拟网络请求
  await Future.delayed(Duration(seconds: 2));
  return 'Data from server';
}

void main() {
  fetchData().then((data) {
    print(data);
  }).catchError((error) {
    print('Error: $error');
  });
}

3.2. Stream

Stream represents a series of asynchronous events. Unlike Future, Stream can produce multiple values ​​rather than a single result.

To create a Stream, you can use StreamController:

Stream<int> countStream() {
  final controller = StreamController<int>();
  Timer.periodic(Duration(seconds: 1), (timer) {
    controller.add(timer.tick);
  });
  return controller.stream;
}

void main() {
  countStream().listen((count) {
    print('Count: $count');
  });
}

This is just a basic introduction to asynchronous processing. Flutter also provides more asynchronous tools and functions, such as async* and await for, which can handle asynchronous operations more conveniently. In-depth learning of asynchronous processing requires practice and continuous attempts. I hope you can master these technologies in actual projects.

4. HTTP requests and Rest API

In modern applications, interacting with the server is a very common requirement. Flutter provides multiple ways to make HTTP requests and handle Rest APIs.

4.1. Using the http package

Flutter's http package is a simple and easy-to-use HTTP request library that allows you to send HTTP requests and process responses.

First, add the dependency of the http package to the pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter
  http: ^0.13.3

Then, you can use the http package to send HTTP requests:

import 'package:http/http.dart' as http;

Future<void> fetchData() async {
  final url = Uri.parse('https://api.example.com/data');
  final response = await http.get(url);

  if (response.statusCode == 200) {
    print('Response: ${response.body}');
  } else {
    print('Error: ${response.statusCode}');
  }
}

4.2. Using the Dio package

dio is another popular HTTP request library, which provides richer functions and easy-to-use API.

First, add the dependency of the dio package to the pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter
  dio: ^4.0.0

Then, you can use the dio package to send HTTP requests:

import 'package:dio/dio.dart';

Future<void> fetchData() async {
  final dio = Dio();
  final url = 'https://api.example.com/data';
  final response = await dio.get(url);

  if (response.statusCode == 200) {
    print('Response: ${response.data}');
  } else {
    print('Error: ${response.statusCode}');
  }
}

4.3. Processing JSON data

Usually the data returned by the server is in JSON format. In Flutter, you can use the dart:convert package to parse and serialize JSON data.

import 'dart:convert';

void main() {
  final jsonString = '{"name": "John", "age": 30}';
  final jsonData = jsonDecode(jsonString);

  print('Name: ${jsonData['name']}');
  print('Age: ${jsonData['age']}');
}

This is just a basic introduction to HTTP requests and processing JSON data. In a real project, you may also need to handle errors, use model classes to serialize data, etc. I hope you can learn more about HTTP requests and Rest API by studying the official documentation and sample code.

5. Data persistence

Data persistence is essential in applications, and Flutter provides a variety of ways to achieve local storage of data.

5.1. Using the shared_preferences package

shared_preferences is an easy-to-use local repository that stores key-value data.

First, add the dependency of the shared_preferences package to the pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter
  shared_preferences: ^2.0.9

The data can then be read and written using the shared_preferences package:

import 'package:shared_preferences/shared_preferences.dart';

void main() async {
  final prefs = await SharedPreferences.getInstance();

  // 保存数据
  await prefs.setString('username', 'John');

  // 读取数据
  final username = prefs.getString('username');
  print('Username: $username');
}

5.2. Using the sqflite package

sqflite is a SQLite database package that provides more powerful database functions and is suitable for scenarios where complex data needs to be stored.

First, add the dependency of the sqflite package to the pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter
  sqflite: ^2.0.0+4

You can then use the sqflite package to create and manage the database:

import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

void main() async {
  final databasePath = await getDatabasesPath();
  final database = await openDatabase(
    join(databasePath, 'app_database.db'),
    version: 1,
    onCreate: (db, version) {
      db.execute('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
    },
  );

  // 插入数据
  await database.insert('users', {'name': 'John'});

  // 查询数据
  final users = await database.query('users');
  for (var user in users) {
    print('User: ${user['name']}');
  }
}

This is just a basic introduction to data persistence. In actual projects, you may also need to handle database migration, use ORM framework, etc. I hope you can learn more about data persistence by studying the official documentation and sample code.

6. Comprehensive Demo

The following is a comprehensive sample code that includes navigation and routing, state management, asynchronous processing, HTTP requests and Rest API, and data persistence. This example will use a Provider to manage state and obtain data via HTTP requests and save it to a SQLite database.

First, add dependencies in the pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
  provider: ^6.0.1
  http: ^0.13.3
  sqflite: ^2.0.0+4

Then, create four Dart files to build the example:

main.dart: Define MyApp as the root Widget and create MaterialApp.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:http/http.dart' as http;

import 'home_page.dart';
import 'user.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (context) => UserProvider()),
        ],
        child: HomePage(),
      ),
    );
  }
}

home_page.dart: Define HomePage as the page that displays user information.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'user.dart';
import 'second_page.dart';

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final userProvider = Provider.of<UserProvider>(context);
    final users = userProvider.users;

    return Scaffold(
      appBar: AppBar(
        title: Text('User List'),
      ),
      body: ListView.builder(
        itemCount: users.length,
        itemBuilder: (context, index) {
          final user = users[index];
          return ListTile(
            title: Text(user.name),
            subtitle: Text('Email: ${user.email}'),
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => SecondPage(user: user),
                ),
              );
            },
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          await userProvider.fetchUsersFromApi();
        },
        child: Icon(Icons.refresh),
      ),
    );
  }
}

second_page.dart: Define SecondPage as a page that displays information for a single user.

import 'package:flutter/material.dart';

import 'user.dart';

class SecondPage extends StatelessWidget {
  final User user;

  SecondPage({required this.user});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('User Detail'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(user.name, style: TextStyle(fontSize: 24)),
            SizedBox(height: 10),
            Text('Email: ${user.email}', style: TextStyle(fontSize: 18)),
            SizedBox(height: 10),
            Text('Phone: ${user.phone}', style: TextStyle(fontSize: 18)),
            SizedBox(height: 10),
            Text('Website: ${user.website}', style: TextStyle(fontSize: 18)),
          ],
        ),
      ),
    );
  }
}

user.dart: Defines the User class and UserProvider for state management and data persistence.

import 'package:flutter/material.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:http/http.dart' as http;
import 'dart:convert'; // 添加此导入

class User {
  final String name;
  final String email;
  final String phone;
  final String website;

  User(
      {required this.name,
      required this.email,
      this.phone = '',
      this.website = ''});
}

class UserProvider extends ChangeNotifier {
  List<User> _users = [];

  List<User> get users => _users;

  Future<void> fetchUsersFromApi() async {
    final response =
        await http.get(Uri.parse('https://jsonplaceholder.typicode.com/users'));
    if (response.statusCode == 200) {
      final List<dynamic> data = json.decode(response.body); // 使用json.decode方法
      _users = data
          .map((item) => User(
              name: item['name'],
              email: item['email'],
              phone: item['phone'],
              website: item['website']))
          .toList();
      notifyListeners();
      saveUsersToDatabase();
    }
  }

  Future<void> saveUsersToDatabase() async {
    final dbPath = await getDatabasesPath();
    final database = await openDatabase(join(dbPath, 'user_database.db'),
        version: 1, onCreate: (db, version) {
      db.execute(
        'CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT, phone TEXT, website TEXT)',
      );
    });

    await database.delete('users');
    for (var user in _users) {
      await database.insert('users', {
        'name': user.name,
        'email': user.email,
        'phone': user.phone,
        'website': user.website
      });
    }
  }

  Future<void> loadUsersFromDatabase() async {
    final dbPath = await getDatabasesPath();
    final database =
        await openDatabase(join(dbPath, 'user_database.db'), version: 1);

    final List<Map<String, dynamic>> maps = await database.query('users');
    _users = maps
        .map((map) => User(
            name: map['name'],
            email: map['email'],
            phone: map['phone'],
            website: map['website']))
        .toList();
    notifyListeners();
  }
}

This example will obtain user data through HTTP requests and use a Provider to manage user data. User data will be saved in a SQLite database and loaded from the database when the application is started.

Guess you like

Origin blog.csdn.net/udisi658996666/article/details/132641643