<repo_name>shxx/dva-reactnative-starter<file_sep>/RnDva/src/components/common/CustomNavigationBar.js
import React from 'react';
import { View, Image, TouchableOpacity, Text } from 'react-native';
import { Dim } from '../../utils/config';
import { Actions } from 'react-native-router-flux';

const CustomNavigationBar = ({ navigationBarColor, backImageUri, title, titleColor = '#fff', onRightPress, rightImageUri, rightText }) => {
  return (
    <View>
      <View style={[ styles.statusBarStyle, { backgroundColor: navigationBarColor } ]} />
      <View style={[ styles.navigationBarStyle, { backgroundColor: navigationBarColor } ]}>
        <TouchableOpacity onPress={Actions.pop} style={(onRightPress && rightText) ? styles.backContainerStyle : ''}>
          <Image style={styles.backButtonStyle}
            source={{ uri: backImageUri }} />
        </TouchableOpacity>
        <Text ellipsizeMode='tail' numberOfLines={1} style={[
          styles.titleStyle,
          { color: titleColor }
        ]}>{title}</Text>

        {
          onRightPress ? <TouchableOpacity onPress={onRightPress}>
          {
            rightText ? <Text style={[ styles.rightTextStyle, { color: titleColor } ]}
            ellipsizeMode='tail' numberOfLines={1}>
              {rightText}
            </Text> : <Image style={styles.rightImageStyle} source={{ uri: rightImageUri }} />
          }
          </TouchableOpacity> : <View style={styles.rightImageStyle} />
        }
      </View>
    </View>
  );
};

const styles = {
  statusBarStyle: {
    height: 20*Dim.Factor
  },
  navigationBarStyle: {
    width: Dim.Width,
    height: 44*Dim.Factor,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'flex-start'
  },
  backButtonStyle: {
    width: 20*Dim.Factor,
    height: 20*Dim.Factor,
    marginLeft: 7*Dim.Factor
  },
  titleStyle: {
    flex: 1,
    textAlign: 'center',
    fontSize: 18*Dim.Factor,
    color: '#fff'
  },
  rightImageStyle: {
    width: 20*Dim.Factor,
    height: 20*Dim.Factor,
    marginRight: 7*Dim.Factor
  },
  rightTextStyle: {
    width: 80*Dim.Factor,
    textAlign: 'right',
    fontSize: 16*Dim.Factor,
    color: '#fff',
    paddingRight: 7*Dim.Factor
  },
  backContainerStyle: {
    width: 80*Dim.Factor,
    justifyContent: 'center',
    alignItems: 'flex-start'
  }
};

export { CustomNavigationBar };
<file_sep>/RnDva/src/components/common/LoginButton.js
import React from 'react';
import { Text, TouchableOpacity } from 'react-native';
import { Dim } from '../../utils/config';

const LoginButton = ({ onPress, title, enable = true, customStyles }) => {
  return (
    <TouchableOpacity
      onPress={onPress}
      style={[ styles.buttonStyle, customStyles ]}
      disabled={!enable}
      >
      <Text style={{
        fontSize: 18*Dim.Factor,
        color: enable ? '#fff' : 'rgba(255,255,255,0.5)'
      }}>
       {title}
      </Text>
    </TouchableOpacity>
  );
};

const styles = {
  buttonStyle: {
    width: Dim.Width-44*Dim.Factor,
    height: 44*Dim.Factor,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: '#e54748',
    borderRadius: 4*Dim.Factor
  }
};

export { LoginButton };
<file_sep>/RnDva/src/routes/Register2.js
import React from 'react';
import { StyleSheet, View, Text, Button, TextInput, TouchableOpacity, Image, StatusBar, ScrollView } from 'react-native';
import { connect } from 'dva/mobile';
import { Actions } from 'react-native-router-flux';
import { Dim } from '../utils/config';
import { LoginButton, CustomNavigationBar } from '../components/common';
import { lineIcoStyle, lineDividerStyle, inputStyle, inputTitleStyle } from '../utils/defaults';

class Register2 extends React.Component {

  constructor(props, context) {
    super(props, context);

    this.state = {
      captcha: '',
      referrer: '',
      enable: false
    };
  }

  handleCaptcha(text) {
    this.setState({
      captcha: text,
      enable: text.length > 0 ? true : false
    })
  }

  handleReferrer(text) {
    this.setState({
      referrer: text,
    })
  }

  getCaptcha() {
    this.props.dispatch({
      type: 'Newbie/getCaptcha'
    })
  }

  onRegister() {
    const { captcha, referrer } = this.state;
    this.props.dispatch({
      type: 'Newbie/onRegister',
      payload: {
        captcha: captcha,
        referrer: referrer
      }
    });
  }

  render() {
    const { phone, running, countdown } = this.props;
    const { captcha, referrer, enable } = this.state;

    return (
      <View style={styles.container}>
        <StatusBar barStyle='default' />
        <CustomNavigationBar navigationBarColor='#f9f9f9' backImageUri='back_lightgray' title='输入验证码' titleColor='#090904'  />

        <View style={styles.container}>
          <View>
            <View style={{ marginTop: 40*Dim.Factor, width: Dim.Width-44*Dim.Factor }}>
              <Text style={{ fontSize: 16*Dim.Factor, color: '#432f2f' }}>已发送验证码至</Text>
            </View>
            <View style={styles.phoneContainer}>
              <Text style={{ fontSize: 26*Dim.Factor, color: '#090404' }}>{phone}</Text>
            </View>
          </View>
          <View>
            <View style={[ styles.inputContainer, { marginTop: 93*Dim.Factor } ]}>
              <View style={styles.inputRowContainer}>
                <Text style={inputTitleStyle}>验证码</Text>
                <TextInput
                  clearButtonMode='while-editing'
                  style={[ inputStyle, { marginLeft: 20*Dim.Factor } ]}
                  value={captcha}
                  placeholder="6位数字"
                  placeholderTextColor='#b3a4a4'
                  maxLength={6}
                  keyboardType='number-pad'
                  onChangeText={(text) => this.handleCaptcha(text)} />

                <View style={styles.splitLine} />

                <TouchableOpacity
                  onPress={() => this.getCaptcha()}
                  disabled={running}>
                  <Text style={{ fontSize: 14*Dim.Factor, color: running ? '#766a6a' : '#e54748'}}>
                    { running ? `${countdown} 后重新获取` : '点击重新获取' }
                  </Text>
                </TouchableOpacity>
              </View>
              <View style={[ lineDividerStyle, { backgroundColor: '#ccc', width: Dim.Width-44*Dim.Factor } ]} />
            </View>

            <View style={[ styles.inputContainer, { marginTop: 10*Dim.Factor } ]}>
              <View style={styles.inputRowContainer}>
                <Text style={inputTitleStyle}>推荐人</Text>
                <TextInput
                  clearButtonMode='while-editing'
                  style={[ inputStyle, { marginLeft: 20*Dim.Factor } ]}
                  value={referrer}
                  placeholder="选填"
                  placeholderTextColor='#b3a4a4'
                  keyboardType='number-pad'
                  onChangeText={(text) => this.handleReferrer(text)} />
              </View>
              <View style={[ lineDividerStyle, { backgroundColor: '#ccc', width: Dim.Width-44*Dim.Factor } ]} />
            </View>
          </View>

          <LoginButton onPress={() => this.onRegister()} title='完成' enable={enable} customStyles={{ marginTop: 65*Dim.Factor }} />

        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'column',
    alignItems: 'center'
  },
  inputContainer: {
    height: 47*Dim.Factor
  },
  inputRowContainer: {
    flexDirection: 'row',
    height: 46*Dim.Factor,
    alignItems: 'center'
  },
  phoneContainer: {
    marginTop: 20*Dim.Factor,
    alignItems: 'center'
  },
  splitLine: {
    width: 1,
    height: 20*Dim.Factor,
    marginRight: 10*Dim.Factor,
    backgroundColor: '#ccc'
  }
});


const mapStateToProps = ({ Newbie }) => {
  const { phone, running, countdown } = Newbie;
  return { phone, running, countdown };
};

export default connect(mapStateToProps)(Register2);
<file_sep>/RnDva/src/Router.js
import React from 'react';
import { connect } from 'dva/mobile';
import { Scene, Router, Actions } from 'react-native-router-flux';
import { StyleSheet, Text, Image, View } from 'react-native';

import Splash from './components/Splash';

import Homepage from './routes/Homepage';
import Find from './routes/Find';
import More from './routes/More';

import Login from './routes/Login';
import Register from './routes/Register';
import Register2 from './routes/Register2';

class TabIcon extends React.PureComponent {
  render() {
    const { selected, title } = this.props;

    var title_zh, ico, ico_selected = undefined;
    switch (title) {
      case '首页':
        ico = require('../assets/homepage.png');
        ico_selected = require('../assets/homepage_icon.png');
        break;
      case '发现':
        ico = require('../assets/find.png');
        ico_selected = require('../assets/find_icon.png');
        break;
      case '更多':
        ico = require('../assets/more.png');
        ico_selected = require('../assets/more_icon.png');
        break;
      default:
    }

    return (
      <View style={styles.tabIcon}>
        <Image style={styles.tabIconIco} source={ selected ? ico : ico_selected } />
        <Text style={[ styles.tabIconTitle, { color: selected ? '#e54748' : '#766a6a' } ]}
          >
          {title}
        </Text>
      </View>

        );
    }
}

const RouterComponent = ({ dispatch }) => {

  function onLogout() {
    dispatch({ type: 'Auth/logout' });
  }

  return (
    <Router>

      <Scene
        key="splash"
        hideNavBar
        passProps
        splashText="Hello RnDva"
        component={Splash}
      />

      <Scene key="auth">
        <Scene key="login" component={Login} hideNavBar />
      </Scene>

      <Scene key="tabbar" tabs={true} hideNavBar tabBarStyle={styles.tabBar}>

        <Scene
          key="homepage"
          title="首页"
          icon={TabIcon}
          initial
         >
            <Scene key="public" component={Homepage} hideNavBar />
            <Scene key="authorized" component={Homepage} hideNavBar />
        </Scene>

        <Scene key="find" title="发现" component={Find} icon={TabIcon} />
        <Scene key="more" title="更多" component={More} icon={TabIcon} />
      </Scene>

      <Scene key="register" component={Register} hideNavBar />
      <Scene key="register2" component={Register2} hideNavBar />

    </Router>
  );
};

let styles = StyleSheet.create({
  tabBar: {
      borderTopWidth : 1,
      borderColor : '#ccc'
  },
  tabIcon: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  },
  tabIconIco: {
    width: 22,
    height: 22
  },
  tabIconTitle: {
    marginTop: 5,
    fontSize: 10
  },
  redNav: {
    backgroundColor: '#E44747'
  },
  redNavTitle: {
    color: '#fff'
  },
  redNavRightButtonText: {
    color: '#fff',
    fontSize: 14,
    marginTop: 3
  },
  whiteNav: {
    backgroundColor: '#fff',
    borderBottomColor: '#ddd',
  },
  whiteNavTitle: {
    color: '#090404'
  },
});

export default connect()(RouterComponent);
<file_sep>/RnDva/src/utils/config.js
import Dimensions from 'Dimensions';

var debug = true;
var needCheckVersion = false;

var { height, width } = Dimensions.get('window');

module.exports = {
  Dim: {
    Width: width,
    Height: height,
    Factor: width/375
  }
};
<file_sep>/RnDva/src/routes/Homepage.js
import React, { Component } from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import { connect } from 'dva/mobile';
import { Actions } from 'react-native-router-flux';
import { Motion, spring } from 'react-motion';
import { Dim } from '../utils/config';

class HomepageComponent extends Component {

  constructor(props, context) {
    super(props, context);

    this.state = {
      open: '',
    };
  }

  handleClick() {
    this.setState({
      open: !this.state.open
    });
  }

  render() {
    return (
      <View style={styles.container}>
        <Text>This is homepage.</Text>
        <Button title="login" onPress={Actions.auth} />
        <Button title="register" onPress={Actions.register} />

        <Button title="react-motion in rn, pls click it"
            onPress={() => this.handleClick()} />
        <Motion style={{
            x: spring(this.state.open ? Dim.Width-50 : 0),
            a: spring(this.state.open ? 1 : 0.2)
          }}>
          {({x, a}) =>
            <View style={{ width: Dim.Width, height: 50, backgroundColor: '#f9f9f9' }}>
              <View style={{
                  width: 50,
                  height: 50,
                  position: 'absolute',
                  left: x,
                  opacity: a,
                  backgroundColor: '#ffc2da' }}>
              </View>
            </View>
          }
        </Motion>
      </View>
    );
  }
}

let styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  },
});

const mapStateToProps = ({ Homepage }) => {
  return {};
};

export default connect(mapStateToProps)(HomepageComponent);
<file_sep>/RnDva/src/components/common/index.js
export * from './LoginButton';
export * from './CustomNavigationBar';
<file_sep>/RnDva/src/models/Newbie.js
import { delay } from "../services/Countdown";

const INITIAL_STATE = {
  phone: '',
  password: '',
  secure: true,
  enable: false,
  checked: true,

  running: false,
  countdown: 0,

  loading: false
};

export default {
  namespace: 'Newbie',
  state: { ...INITIAL_STATE },
  reducers: {
    showLoading(state) {
      return { ...state, loading: true, error: '' };
    },
    hideLoading(state) {
      return { ...state, loading: false, error: '' };
    },

    phoneChanged(state, action) {
      return { ...state,
        phone: action.payload,
        enable: (action.payload.length == 11 && state.password.length >= 6) ? true : false
      };
    },
    passwordChanged(state, action) {
      return { ...state,
        password: action.payload,
        enable: (action.payload.length >= 6 && state.phone.length == 11) ? true : false
      };
    },
    secureChanged(state, action) {
      return { ...state, secure: !state.secure };
    },
    checkedChanged(state, action) {
      return { ...state, checked: !state.checked };
    },

    getCaptchaSuccess(state, action) {
      return {
        ...state,
        running: true,
        countdown: 60
      }
    },
    countdown(state, action) {
      return { ...state, countdown: action.payload };
    },
    stopCountdown(state) {
      return { ...state, running: false, countdown: 0 };
    },
  },
  effects: {
    *getCaptcha ({
      payload
    }, {call, put, select}) {
      yield put({type: 'showLoading'})

      const ret = { data: { result_code: 'SUCCESS' } } // 假定获取验证码成功
      if (ret.data.result_code === "SUCCESS") {
        yield put({type: 'getCaptchaSuccess'});
        yield put({type: 'hideLoading'})

        yield delay(1000); // 补1s

        while (true) {
          let countdown = yield select(state => state.Newbie.countdown);
          if (countdown > 0) {
            countdown--
            yield put({ type: 'countdown', payload: countdown });
            yield delay(1000);
          } else {
            yield put({ type: 'stopCountdown' });
            return;
          }
        }
      } else {
        yield put({type: 'hideLoading'})
      }
    },
    * onRegister ({
      payload
    }, {call, put}) {
      console.log('onRegister', payload);
    }
  },
  subscriptions: {

  }
};
<file_sep>/RnDva/src/components/Login.js
import React from 'react';
import {
  View,
  Text,
  TextInput,
  TouchableOpacity,
  Image,
  StatusBar
} from 'react-native';
import { Actions } from 'react-native-router-flux';
import { Dim } from '../utils/config';
import { LoginButton, CustomNavigationBar } from './common';
import { lineIcoStyle, lineDividerStyle, inputStyle } from '../utils/defaults';

const LoginComponent = ({ phone, password, secure, enable, handlePhone, handlePassword, onSecure, onLogin }) => {
  return (
    <View style={styles.container}>
      <StatusBar barStyle='default' />
      <CustomNavigationBar navigationBarColor='transparent' backImageUri='close_lightgray' />

      <View style={styles.container}>
        <View style={styles.logoContainer}>
          <Image style={{
            width: 98*Dim.Factor,
            height: 98*Dim.Factor
          }} source={{ uri: 'logo_ico' }} />
        </View>
        <View>
          <View style={styles.inputContainer}>
            <TextInput
              style={inputStyle}
              clearButtonMode='while-editing'
              placeholder="手机号"
              placeholderTextColor='#432f2f'
              keyboardType='number-pad'
              maxLength={11}
              value={phone}
              onChangeText={(text) => handlePhone(text)} />
            <View style={[ lineDividerStyle, { backgroundColor: '#ccc', width: Dim.Width-44*Dim.Factor } ]} />
          </View>

          <View style={[ styles.inputContainer, { marginTop: 10 } ]}>
            <View style={styles.inputRowContainer}>
              <TextInput
                style={inputStyle}
                clearButtonMode='while-editing'
                placeholder="登录密码"
                placeholderTextColor='#432f2f'
                secureTextEntry={secure}
                maxLength={16}
                value={password}
                onChangeText={(text) => handlePassword(text)} />
              <TouchableOpacity onPress={onSecure}>
                <Image style={lineIcoStyle} source={ secure ? { uri: 'eye_close_lightgray' } : { uri: 'eye_open_red' }} />
              </TouchableOpacity>
            </View>
            <View style={[ lineDividerStyle, { backgroundColor: '#ccc', width: Dim.Width-44*Dim.Factor } ]} />
          </View>
        </View>

        <View style={styles.additionalContainer}>
          <TouchableOpacity onPress={Actions.forgot}>
            <Text style={{
              fontSize: 14*Dim.Factor,
              color: '#968d8d'
            }}>忘记密码</Text>
          </TouchableOpacity>
          <TouchableOpacity onPress={Actions.register}>
            <Text style={{
              fontSize: 14*Dim.Factor,
              color: '#e54748'
            }}>注册账户</Text>
          </TouchableOpacity>
        </View>

        <LoginButton onPress={onLogin} title='登录' enable={enable} customStyles={{ marginTop: 65*Dim.Factor }} />
      </View>
    </View>
  );
};

const styles = {
  container: {
    flex: 1,
    alignItems: 'center'
  },
  logoContainer: {
    marginTop: 35*Dim.Factor,
    marginBottom: 55*Dim.Factor,
  },
  inputContainer: {
    height: 47*Dim.Factor
  },
  inputRowContainer: {
    flexDirection: 'row',
    height: 46*Dim.Factor,
    alignItems: 'center'
  },
  additionalContainer: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    width: Dim.Width-44*Dim.Factor,
    marginTop: 20*Dim.Factor
  }
};

export default LoginComponent;
<file_sep>/README.md
# dva-reactnative-starter

This project is designed to help those who use dva to develop an native-app(it also can develop web app). In a word, this is a simple example.

[![NPM version](https://img.shields.io/npm/v/antd-init.svg?style=flat)](https://npmjs.org/package/antd-init)

[Dva](https://github.com/dvajs/dva)

## What's include?

1. [Dva](https://github.com/dvajs/dva), Lightweight front-end framework based on redux, redux-saga and react-router@2.x. (Inspired by elm and choo)

[Dva Knowledgemap](https://github.com/dvajs/dva-knowledgemap)

[Quick Start (12 步 30 分钟，完成用户管理的 CURD 应用) (react+dva+antd)](https://github.com/sorrycc/blog/issues/18)

2. [React-Motion](https://github.com/chenglou/react-motion), A spring that solves your animation problems.

It's Works with React-Native v0.18+. In addition, I have written two examples on the web application. If you are interested in the animation, you should go to look at it. (Additional knowledge.)

[react-motion-tab-animate](https://github.com/yuzhouisme/react-motion-tab-animate)

[react-motion-nav-animate](https://github.com/yuzhouisme/react-motion-nav-animate)

## Screenshot

![Screenshot](https://github.com/yuzhouisme/dva-reactnative-starter/blob/master/Screenshot/screenshot.gif)

## Getting started

1. Clone this repo.

2. Modify something in package.json like name to "your-project-name".

3. Run npm install.

```bash
$ npm install
```

4. Run.

```bash
$ react-native run-ios
```

## Explanation

1. To know the directory structure.

```
$ tree . -L 3
.
├── android
├── assets // saved image resources who variable
├── index.android.js // root entrance for android platform!!
├── index.ios.js // root entrance!!
├── ios
├── node_modules
├── package.json
├── src
│   ├── Router.js // all routing
├── components // dumb component
│   ├── Login.js
│   ├── Splash.js
│   └── common
├── models
│   ├── Auth.js
│   ├── Find.js
│   ├── Homepage.js
│   ├── Initial.js
│   └── Newbie.js
├── routes // smart component
│   ├── Find.js
│   ├── Homepage.js
│   ├── Login.js
│   ├── More.js
│   ├── Register.js
│   └── Register2.js
├── services
├── utils // such as config, request, defaults
└── yarn.lock
```

2. The whole entrance is index.jsx. I try to describe clearly why import that.

```
...
const app = dva();

// Load models. In web application can use dynamic routing, But in native-app maybe don't need
app.model(Initial);
app.model(Auth);
app.model(Homepage);
app.model(Newbie);
app.model(Find);

app.router(() => <Router />);

AppRegistry.registerComponent('RnDva', () => app.start());
```

3. Dva/Redux/React-Router, reading some documents will help you.

4. Look at 'subscriptions' (in models/Initial.js), when app in Splash screen, then will jump to Homepage after 3s.
```
...
subscriptions: {
  initialize() {
    _.delay(() => {
      Actions.tabbar({ type: 'reset' });
    }, 3000);
  }
}
```

## Inspiration

Thanks to [dvajs group](https://github.com/dvajs) for providing the lightweight framework for developers. And in work I am a mobile application developer as android/iOS platform, before using dva, I have been using the reactnative+reudx+redux-thunk. I have subscribed to issue list for a long time. I saw a lot of issues have been proposed, and soon be solved. Thanks to the team and chencheng (云谦) once again.

Thank you for reading.

## 

logo_ico.png from [rezzzzzzz's Oh My D.VA](https://dribbble.com/rezzzzzz) who in [dribbble](https://dribbble.com). Thank rezzzzzzz! She's so pink :)
<file_sep>/RnDva/index.ios.js
/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React from 'react';
import { AppRegistry } from 'react-native';

import dva from 'dva/mobile';
import Router from './src/Router';

import Initial from './src/models/Initial';
import Auth from './src/models/Auth';
import Homepage from './src/models/Homepage';
import Newbie from './src/models/Newbie';

const app = dva();

app.model(Initial);
app.model(Auth);
app.model(Homepage);
app.model(Newbie);

app.router(() => <Router />);


AppRegistry.registerComponent('RnDva', () => app.start());
<file_sep>/RnDva/src/services/Auth.js
export function signInWithPhoneAndPassword(phone, password) {
  return { user: {}, err: true };
}

export function signOut() {
  return true;
}
<file_sep>/RnDva/src/routes/Register.js
import React from 'react';
import { StyleSheet, View, Text, Button, TextInput, TouchableOpacity, Image, StatusBar, ScrollView } from 'react-native';
import { connect } from 'dva/mobile';
import { Actions } from 'react-native-router-flux';
import { Dim } from '../utils/config';
import { LoginButton, CustomNavigationBar } from '../components/common';
import { lineIcoStyle, lineDividerStyle, inputStyle, inputTitleStyle } from '../utils/defaults';

class Register extends React.Component {

  handlePhone(text) {
    this.props.dispatch({
      type: 'Newbie/phoneChanged',
      payload: text
    })
  }

  handlePassword(text) {
    this.props.dispatch({
      type: 'Newbie/passwordChanged',
      payload: text
    })
  }

  onChecked() {
    this.props.dispatch({
      type: 'Newbie/checkedChanged',
    })
  }

  onSecure() {
    this.props.dispatch({
      type: 'Newbie/secureChanged',
    })
  }

  render() {
    const { phone, password, secure, enable, checked } = this.props;

    return (
      <View style={styles.container}>
        <StatusBar barStyle='default' />
        <CustomNavigationBar navigationBarColor='transparent' backImageUri='close_lightgray' />

        <View style={styles.container}>
          <View style={styles.logoContainer}>
            <Image style={{
              width: 98*Dim.Factor,
              height: 98*Dim.Factor
            }} source={{ uri: 'logo_ico' }} />
          </View>

          <View>
            <View style={styles.inputContainer}>
              <View style={styles.inputRowContainer}>
                <Text style={inputTitleStyle}>
                  { "手机号    " }
                </Text>
                <TextInput
                  clearButtonMode='while-editing'
                  style={[ inputStyle, { fontSize: 14*Dim.Factor, marginLeft: 20*Dim.Factor } ]}
                  value={phone}
                  keyboardType='number-pad'
                  maxLength={11}
                  placeholder="中国大陆11手机号码"
                  placeholderTextColor='#b3a4a4'
                  onChangeText={(text) => this.handlePhone(text)}
                  />
              </View>
              <View style={[ lineDividerStyle, { backgroundColor: '#ccc', width: Dim.Width-44*Dim.Factor } ]} />
            </View>
            <View style={styles.inputContainer}>
              <View style={styles.inputRowContainer}>
                <Text style={inputTitleStyle}>登录密码</Text>
                <TextInput
                  clearButtonMode='while-editing'
                  style={[ inputStyle, { fontSize: 14*Dim.Factor, marginLeft: 20*Dim.Factor } ]}
                  value={password}
                  secureTextEntry={secure}
                  maxLength={16}
                  placeholder="6～16位数字、字母或符号组合"
                  placeholderTextColor='#b3a4a4'
                  returnKeyType='done'
                  onChangeText={(text) => this.handlePassword(text)}
                />
                <TouchableOpacity onPress={() => this.onSecure()}>
                  <Image style={lineIcoStyle} source={ secure ? { uri: 'eye_close_lightgray' } : { uri: 'eye_open_red' }} />
                </TouchableOpacity>
              </View>
              <View style={[ lineDividerStyle, { backgroundColor: '#ccc', width: Dim.Width-44*Dim.Factor } ]} />
            </View>
          </View>

          <LoginButton onPress={Actions.register2} title='下一步' enable={enable} customStyles={{ marginTop: 65*Dim.Factor }} />

          <View style={styles.tipsContainer}>
            <TouchableOpacity onPress={() => this.onChecked()}>
              <Image style={styles.checkImage} source={ checked ? { uri: 'check_colorful_round' } : { uri: 'uncheck_lightgray_round' }} />
            </TouchableOpacity>
            <Text style={styles.tips}>我已阅读并同意</Text>
            <TouchableOpacity>
              <Text style={styles.agreement}>《用户服务协议》</Text>
            </TouchableOpacity>
          </View>
        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'column',
    alignItems: 'center',
  },
  logoContainer: {
    marginTop: 35*Dim.Factor,
    marginBottom: 55*Dim.Factor,
  },
  inputContainer: {
    height: 47*Dim.Factor
  },
  inputRowContainer: {
    flexDirection: 'row',
    height: 46*Dim.Factor,
    alignItems: 'center'
  },
  tips: {
    fontSize: 14*Dim.Factor,
    color: '#968d8d'
  },
  agreement: {
    fontSize: 14*Dim.Factor,
    color: '#e54748'
  },
  tipsContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    marginTop: 15*Dim.Factor,
  },
  checkImage: {
    width: 20*Dim.Factor,
    height: 20*Dim.Factor,
    marginLeft: 4*Dim.Factor,
    marginRight: 2*Dim.Factor
  }
});


const mapStateToProps = ({ Newbie }) => {
  const { phone, password, secure, enable, checked, loading } = Newbie;

  return { phone, password, secure, enable, checked, loading };
};

export default connect(mapStateToProps)(Register);
<file_sep>/RnDva/android/settings.gradle
rootProject.name = 'RnDva'

include ':app'
<file_sep>/RnDva/src/routes/Login.js
import React from 'react';
import { connect } from 'dva/mobile';
import { Actions } from 'react-native-router-flux';
import LoginComponent from '../components/Login';

class Login extends React.Component {
  render() {
    const { phone, password, secure, enable, dispatch } = this.props;

    const loginProps = {
      phone,
      password,
      secure,
      enable,
      handlePhone(text) {
        dispatch({
          type: 'Auth/phoneChanged',
          payload: text
        })
      },
      handlePassword(text) {
        dispatch({
          type: 'Auth/passwordChanged',
          payload: text
        })
      },
      onSecure() {
        console.log('onSecure');
        dispatch({
          type: 'Auth/secureChanged',
        })
      },
      onLogin() {
        console.log('onLogin');
        dispatch({
          type: 'Auth/login',
        });
      }
    };
    return (
      <LoginComponent {...loginProps} />
    );
  }
}

const mapStateToProps = ({ Auth }) => {
  const { phone, password, secure, enable, loading } = Auth;
  return { phone, password, secure, enable, loading };
};

export default connect(mapStateToProps)(Login);
