instaread_sdk 1.0.2
instaread_sdk: ^1.0.2 copied to clipboard
Instaread audio player for Flutter apps on iOS and Android. Wraps the native Instaread iOS and Android SDKs.
example/lib/main.dart
import 'dart:io' show Platform;
import 'package:flutter/material.dart';
import 'package:instaread_sdk/instaread_sdk.dart';
import 'package:permission_handler/permission_handler.dart';
// Same end-to-end harness as the React Native SDK's E2ERN app: two article
// tabs, a 60pt bottom bar the mini player must sit above, and a Settings tab
// that re-configures the SDK (dark, single-player mode, compact design) and
// stands in for the app's own audio player.
const a1 =
'https://instaread.co/insights/sports-recreation-biographies/the-blind-side-book/4ogennxfdc';
const a2 =
'https://instaread.co/insights/health-fitness-diet-nutrition/food-rules-book/lbjab1zcgb';
const bar = 60.0;
const publication = 'instareadsdktest';
void main() {
// Once, at app start, before anything renders.
InstareadPlayer.configure(publication, miniPlayerBottomOffset: bar);
if (Platform.isAndroid) {
// Android 13+: without it the lock-screen media controls never appear.
Permission.notification.request();
}
runApp(const E2EApp());
}
class E2EApp extends StatefulWidget {
const E2EApp({super.key});
@override
State<E2EApp> createState() => _E2EAppState();
}
class _E2EAppState extends State<E2EApp> {
int tab = 0;
bool dark = false;
bool single = false;
InstareadPlayerType? type;
String log = 'log:';
@override
void initState() {
super.initState();
InstareadPlayer.onPlaybackStarted = () =>
setState(() => log += ' [SDK started->own paused]');
}
void re() => InstareadPlayer.configure(
publication,
miniPlayerBottomOffset: bar,
dark: dark,
singlePlayerMode: single,
playerType: type,
);
Widget article(String title, String url) => SingleChildScrollView(
key: ValueKey(url),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: const TextStyle(fontSize: 24)),
InstareadPlayerView(
articleUrl: url,
onReady: () => setState(() => log += ' [ready]'),
onError: () => setState(() => log += ' [error]'),
),
Text('Body ' * 1500),
],
),
);
Widget button(String id, String label, VoidCallback onTap) => Padding(
padding: const EdgeInsets.all(4),
child: Semantics(
identifier: id,
child: TextButton(
style: TextButton.styleFrom(backgroundColor: const Color(0xFFDDDDDD)),
onPressed: onTap,
child: Text(label),
),
),
);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: dark ? const Color(0xFF111111) : Colors.white,
// Like the E2ERN app's SafeAreaView: the 60pt bar sits above the home
// indicator, which the SDK already allows for, so miniPlayerBottomOffset
// is just the bar's own height.
body: SafeArea(
child: Column(
children: [
Expanded(
child: switch (tab) {
0 => article('Article 1 (Flutter tab 1)', a1),
1 => article('Article 2 (Flutter tab 2)', a2),
_ => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
button('dark', 'toggle dark', () {
setState(() => dark = !dark);
re();
}),
button('single', 'toggle single', () {
setState(() => single = !single);
re();
}),
button('compact', 'compact', () {
setState(() => type = InstareadPlayerType.compact);
re();
}),
button('own', 'Start OWN player', () {
InstareadPlayer.stop();
setState(() => log += ' [own->stop]');
}),
Text('dark=$dark single=$single type=${type?.name}'),
Text(log),
],
),
},
),
Container(
height: bar,
color: const Color(0xFFEEEEEE),
child: Row(
children: [
for (final (i, name) in [
'Tab1',
'Tab2',
'Settings',
].indexed)
Expanded(
child: InkWell(
onTap: () => setState(() => tab = i),
child: Center(child: Text(name)),
),
),
],
),
),
],
),
),
),
);
}
}