mirror of
https://github.com/flutter/samples.git
synced 2025-11-10 14:58:34 +00:00
restructured the add to app samples (#698)
This commit is contained in:
100
add_to_app/books/flutter_module_books/lib/api.dart
Normal file
100
add_to_app/books/flutter_module_books/lib/api.dart
Normal file
@@ -0,0 +1,100 @@
|
||||
// Autogenerated from Pigeon (v0.1.0), do not edit directly.
|
||||
// See also: https://pub.dev/packages/pigeon
|
||||
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import
|
||||
import 'dart:async';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class Book {
|
||||
String title;
|
||||
String subtitle;
|
||||
String author;
|
||||
String description;
|
||||
String publishDate;
|
||||
int pageCount;
|
||||
// ignore: unused_element
|
||||
Map<dynamic, dynamic> _toMap() {
|
||||
final Map<dynamic, dynamic> pigeonMap = <dynamic, dynamic>{};
|
||||
pigeonMap['title'] = title;
|
||||
pigeonMap['subtitle'] = subtitle;
|
||||
pigeonMap['author'] = author;
|
||||
pigeonMap['description'] = description;
|
||||
pigeonMap['publishDate'] = publishDate;
|
||||
pigeonMap['pageCount'] = pageCount;
|
||||
return pigeonMap;
|
||||
}
|
||||
|
||||
// ignore: unused_element
|
||||
static Book _fromMap(Map<dynamic, dynamic> pigeonMap) {
|
||||
final Book result = Book();
|
||||
result.title = pigeonMap['title'];
|
||||
result.subtitle = pigeonMap['subtitle'];
|
||||
result.author = pigeonMap['author'];
|
||||
result.description = pigeonMap['description'];
|
||||
result.publishDate = pigeonMap['publishDate'];
|
||||
result.pageCount = pigeonMap['pageCount'];
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class FlutterBookApi {
|
||||
void displayBookDetails(Book arg);
|
||||
static void setup(FlutterBookApi api) {
|
||||
{
|
||||
const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
|
||||
'dev.flutter.pigeon.FlutterBookApi.displayBookDetails',
|
||||
StandardMessageCodec());
|
||||
channel.setMessageHandler((dynamic message) async {
|
||||
final Map<dynamic, dynamic> mapMessage =
|
||||
message as Map<dynamic, dynamic>;
|
||||
final Book input = Book._fromMap(mapMessage);
|
||||
api.displayBookDetails(input);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class HostBookApi {
|
||||
Future<void> cancel() async {
|
||||
const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
|
||||
'dev.flutter.pigeon.HostBookApi.cancel', StandardMessageCodec());
|
||||
|
||||
final Map<dynamic, dynamic> replyMap = await channel.send(null);
|
||||
if (replyMap == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
details: null);
|
||||
} else if (replyMap['error'] != null) {
|
||||
final Map<dynamic, dynamic> error = replyMap['error'];
|
||||
throw PlatformException(
|
||||
code: error['code'],
|
||||
message: error['message'],
|
||||
details: error['details']);
|
||||
} else {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> finishEditingBook(Book arg) async {
|
||||
final Map<dynamic, dynamic> requestMap = arg._toMap();
|
||||
const BasicMessageChannel<dynamic> channel = BasicMessageChannel<dynamic>(
|
||||
'dev.flutter.pigeon.HostBookApi.finishEditingBook',
|
||||
StandardMessageCodec());
|
||||
|
||||
final Map<dynamic, dynamic> replyMap = await channel.send(requestMap);
|
||||
if (replyMap == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
details: null);
|
||||
} else if (replyMap['error'] != null) {
|
||||
final Map<dynamic, dynamic> error = replyMap['error'];
|
||||
throw PlatformException(
|
||||
code: error['code'],
|
||||
message: error['message'],
|
||||
details: error['details']);
|
||||
} else {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
}
|
||||
201
add_to_app/books/flutter_module_books/lib/main.dart
Normal file
201
add_to_app/books/flutter_module_books/lib/main.dart
Normal file
@@ -0,0 +1,201 @@
|
||||
// Copyright 2020 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_module_books/api.dart';
|
||||
|
||||
void main() => runApp(MyApp());
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
theme: ThemeData(
|
||||
primaryColor: const Color(0xff6200ee),
|
||||
),
|
||||
home: BookDetail(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
typedef BookReceived = void Function(Book book);
|
||||
|
||||
class FlutterBookApiHandler extends FlutterBookApi {
|
||||
FlutterBookApiHandler(this.callback);
|
||||
|
||||
final BookReceived callback;
|
||||
|
||||
@override
|
||||
void displayBookDetails(Book book) {
|
||||
assert(
|
||||
book != null,
|
||||
'Non-null book expected from FlutterBookApi.displayBookDetails call.',
|
||||
);
|
||||
callback(book);
|
||||
}
|
||||
}
|
||||
|
||||
class BookDetail extends StatefulWidget {
|
||||
const BookDetail({this.hostApi, this.flutterApi});
|
||||
|
||||
// These are the outgoing and incoming APIs that are here for injection for
|
||||
// tests.
|
||||
final HostBookApi hostApi;
|
||||
final FlutterBookApi flutterApi;
|
||||
|
||||
@override
|
||||
_BookDetailState createState() => _BookDetailState();
|
||||
}
|
||||
|
||||
class _BookDetailState extends State<BookDetail> {
|
||||
Book book;
|
||||
|
||||
HostBookApi hostApi;
|
||||
|
||||
FocusNode textFocusNode = FocusNode();
|
||||
TextEditingController titleTextController = TextEditingController();
|
||||
TextEditingController subtitleTextController = TextEditingController();
|
||||
TextEditingController authorTextController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// This `HostBookApi` class instance lets us make outgoing calls to the
|
||||
// platform.
|
||||
hostApi = widget.hostApi ?? HostBookApi();
|
||||
|
||||
// Registering this `FlutterBookApiHandler` class lets us receive incoming
|
||||
// calls from the platform.
|
||||
// TODO(gaaclarke): make the setup method an instance method so it's
|
||||
// injectable https://github.com/flutter/flutter/issues/59119.
|
||||
FlutterBookApi.setup(FlutterBookApiHandler(
|
||||
// The `FlutterBookApi` just has one method. Just give a closure for that
|
||||
// method to the handler class.
|
||||
(Book book) {
|
||||
setState(() {
|
||||
// This book model is what we're going to return to Kotlin eventually.
|
||||
// Keep it bound to the UI.
|
||||
this.book = book;
|
||||
titleTextController.text = book.title;
|
||||
titleTextController.addListener(() {
|
||||
this.book?.title = titleTextController.text;
|
||||
});
|
||||
// Subtitle could be null.
|
||||
// TODO(gaaclarke): https://github.com/flutter/flutter/issues/59118.
|
||||
subtitleTextController.text = book.subtitle ?? '';
|
||||
subtitleTextController.addListener(() {
|
||||
this.book?.subtitle = subtitleTextController.text;
|
||||
});
|
||||
authorTextController.text = book.author;
|
||||
authorTextController.addListener(() {
|
||||
this.book?.author = authorTextController.text;
|
||||
});
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
void clear() {
|
||||
book = null;
|
||||
// Keep focus if going to the home screen but unfocus if leaving
|
||||
// the activity.
|
||||
textFocusNode.unfocus();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Book Details'),
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.clear),
|
||||
// Pressing clear cancels the edit and leaves the activity without
|
||||
// modification.
|
||||
onPressed: () {
|
||||
hostApi.cancel();
|
||||
clear();
|
||||
},
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.check),
|
||||
// Pressing save sends the updated book to the platform.
|
||||
onPressed: () {
|
||||
hostApi.finishEditingBook(book);
|
||||
clear();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: book == null
|
||||
// Draw a spinner until the platform gives us the book to show details
|
||||
// for.
|
||||
? Center(child: CircularProgressIndicator())
|
||||
: Focus(
|
||||
focusNode: textFocusNode,
|
||||
child: ListView(
|
||||
padding: EdgeInsets.all(24),
|
||||
children: [
|
||||
TextField(
|
||||
controller: titleTextController,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
filled: true,
|
||||
hintText: "Title",
|
||||
labelText: "Title",
|
||||
),
|
||||
),
|
||||
SizedBox(height: 24),
|
||||
TextField(
|
||||
controller: subtitleTextController,
|
||||
maxLines: 2,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
filled: true,
|
||||
hintText: "Subtitle",
|
||||
labelText: "Subtitle",
|
||||
),
|
||||
),
|
||||
SizedBox(height: 24),
|
||||
TextField(
|
||||
controller: authorTextController,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
filled: true,
|
||||
hintText: "Author",
|
||||
labelText: "Author",
|
||||
),
|
||||
),
|
||||
SizedBox(height: 32),
|
||||
Divider(),
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
'${book.pageCount} pages ~ published ${book.publishDate}'),
|
||||
),
|
||||
),
|
||||
Divider(),
|
||||
SizedBox(height: 32),
|
||||
Center(
|
||||
child: Text(
|
||||
'BOOK DESCRIPTION',
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
Text(
|
||||
book.description,
|
||||
style: TextStyle(color: Colors.grey.shade600, height: 1.24),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user