1
0
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:
gaaclarke
2021-02-05 10:20:58 -08:00
committed by GitHub
parent ba8ed34582
commit 323c10558d
239 changed files with 931 additions and 256 deletions

View 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
}
}
}

View 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),
),
],
),
),
);
}
}